数据库表可以使用1:1,1:N或N:M关系相互关联。
在greenDAO中,实体使用to-one 或 to-many进行关联。例如,如果要在greenDAO中建模1:n关系,则将具有一对一和多对多关系。但是,请注意,一对一和一对多的关系并不相互关联,所以您必须更新两者。
建模一对一关系
@ToOne注解定义与另一实体(一个实体对象)的关系。将其应用于持有其他实体对象的属性。
在内部,greenDAO需要一个附加属性,指向由joinProperty参数指定的目标实体的ID。如果此参数不存在,则会自动创建一个附加列来保存该键。
@Entity
public class Order {
@Id private Long id;
private long customerId;
@ToOne(joinProperty = "customerId")
private Customer customer;
}
@Entity
public class Customer {
@Id private Long id;
}
一对一关系的getter方法(在此示例中为getCustomer())在其第一次调用时懒加载目标实体。后续调用将立即返回先前加载的对象。
请注意,如果更改外键属性(这里是customerId),则下一次调用getter(getCustomer())将会加载更新ID后的实体。
另外,如果设置了一个新的实体(setCustomer()),外键属性(customerId)也将被更新。
Customer customerA = user.getCustomer();
// change the customer id
user.setCustomerId(customerIdB);
// or set a customer with a different id
user.setCustomer(customerB);
customerB = user.getCustomer();
assert(customerA.getId() != customerB.getId());
注意:要强制加载一个关系,请使用实体DAO类的loadDeep()和queryDeep()。这将加载与单个数据库查询具有所有一对一关系的实体。如果您始终访问相关实体,这对于性能来说非常棒。
建模多关系
@ToMany
定义与一组其他实体(多个实体对象)的关系。将其应用于表示目标实体列表的属性上。被引用的实体必须有一个或多个属性指向拥有@ToMany的实体。
指定关系映射有三种可能性,仅使用其中之一:
- referencedJoinProperty 参数:
在指向该实体的ID的目标实体中指定“外键”属性的名称。
@Entity
public class Customer {
@Id private Long id;
@ToMany(referencedJoinProperty = "customerId")
@OrderBy("date ASC")
private List<Order> orders;
}
@Entity
public class Order {
@Id private Long id;
private Date date;
private long customerId;
}
- joinProperties 参数:
对于更复杂的关系,指定一个@JoinProperty注解列表。每个@JoinProperty需要原始实体中的源属性和目标实体中的引用属性。
@Entity
public class Customer {
@Id private Long id;
@Unique private String tag;
@ToMany(joinProperties = {
@JoinProperty(name = "tag", referencedName = "customerTag")
})
@OrderBy("date ASC")
private List<Site> orders;
}
@Entity
public class Order {
@Id private Long id;
private Date date;
@NotNull private String customerTag;
}
- @JoinEntity注解
涉及另一个连接实体/表的N:M(多对多)关系,将此注解附加在属性上。
@Entity
public class Product {
@Id private Long id;
@ToMany
@JoinEntity(
entity = JoinProductsWithOrders.class,
sourceProperty = "productId",
targetProperty = "orderId"
)
private List<Order> ordersWithThisProduct;
}
@Entity
public class JoinProductsWithOrders {
@Id private Long id;
private Long productId;
private Long orderId;
}
@Entity
public class Order {
@Id private Long id;
}
一旦运行,插件将生成一个getter来解析被引用实体的列表。例如前两种情况:
// return all orders where customerId == customer.getId()
List<Order> orders = customer.getOrders();
加载和更新多关系
对于第一个请求,多关系被懒加载,然后缓存在源实体的一个List对象中。所以随后调用关系的get方法不会查询数据库。
更新多关系需要额外的工作,因为多关系的列表被缓存,当关联的实体被添加到数据库,列表不会更新。以下代码说明了这种行为:
// get the current list of orders for a customer
List<Order> orders1 = customer.getOrders();
// insert a new order for this customer
Order order = new Order();
order.setCustomerId(customer.getId());
daoSession.insert(order);
// get the list of orders again
List<Order> orders2 = customer.getOrders();
// the (cached) list of orders was not updated
// orders1 has the same size as orders2
assert(orders1.size() == orders2.size);
// orders1 is the same object as orders2
assert(orders1.equals(orders2));
因此,要添加新的关联实体,请将它们手动添加到源实体的to-many列表中:
// get the to-many list before inserting the new entity
// otherwise the new entity might be in the list twice
List<Order> orders = customer.getOrders();
// create the new entity
Order newOrder = ...
// set the foreign key property
newOrder.setCustomerId(customer.getId());
// persist the new entity
daoSession.insert(newOrder);
// add it to the to-many list
orders.add(newOrder);
同样,删除关联实体:
List<Order> orders = customer.getOrders();
// remove one of the orders from the database
daoSession.delete(someOrder);
// manually remove it from the to-many list
orders.remove(someOrder);
添加,更新或删除许多关联实体时,可以使用reset方法清除缓存列表。接下来的get将重新查询关联实体:
// clear any cached list of related orders
customer.resetOrders();
List<Order> orders = customer.getOrders();
双向1:N关系
有时想要在两个方向上浏览1:N关系。在greenDAO中,必须添加一对一和多对多关系来实现此目的。
以下示例显示了客户和订单实体的完整建模,我们以此为例。这一次,我们使用customerId属性来创建两个关系:
@Entity
public class Customer {
@Id private Long id;
@ToMany(referencedJoinProperty = "customerId")
@OrderBy("date ASC")
private List<Order> orders;
}
@Entity
public class Order {
@Id private Long id;
private Date date;
private long customerId;
@ToOne(joinProperty = "customerId")
private Customer customer;
}
假设我们有一个订单实体。使用这两种关系,我们可以得到客户和客户所做的所有订单:
List<Order> allOrdersOfCustomer = order.getCustomer().getOrders();
示例:建模树关系
可以通过建模具有指向自身的一对一和一对多关系的实体来建模树关系:
@Entity
public class TreeNode {
@Id private Long id;
private Long parentId;
@ToOne(joinProperty = "parentId")
private TreeNode parent;
@ToMany(referencedJoinProperty = "parentId")
private List<TreeNode> children;
}
生成的实体允许您导航其父和子节点:
TreeNode parent = entity.getParent();
List<TreeNode> children = entity.getChildren();