对于想了解具有@OneToMany属性的可嵌入实体的读者,本文将提供新的信息,并且为您提供关于@ManyToMany/@OneToManymappingby属性用于双向关联、@OneToMany、@M
对于想了解具有@OneToMany属性的可嵌入实体的读者,本文将提供新的信息,并且为您提供关于@ ManyToMany / @ OneToMany mappingby属性用于双向关联、@OneToMany、@ManyToOne以及@ManyToMany讲解、@OneToMany、@ManyToOne以及@ManyToMany讲解(五)、Django笔记七之ManyToMany和OneToOne介绍的有价值信息。
本文目录一览:- 具有@OneToMany属性的可嵌入实体
- @ ManyToMany / @ OneToMany mappingby属性用于双向关联
- @OneToMany、@ManyToOne以及@ManyToMany讲解
- @OneToMany、@ManyToOne以及@ManyToMany讲解(五)
- Django笔记七之ManyToMany和OneToOne介绍
具有@OneToMany属性的可嵌入实体
说,我有以下实体:
@Entitypublic class A { @Id @GeneratedValue private Long id; @Embedded private B b; //getters and setters}@Embeddablepublic class B { @OneToMany private List<C> cList; //getters and setters}@Entitypublic class C { @Id @GeneratedValue private Long id; //other fields, getters and setters}
通过结合使用模式自动生成功能,Hibernate
我得到了一个附加表,其中包含A
和之间的映射C
。但是,我想实现通过添加一个一对多的关系A
的
编号 为C
( 例如,没有附加表 )。
这可能吗?如果是,我应该使用什么注释来创建这种映射?
答案1
小编典典通常,使用@JoinColumn批注是可能的。它也适用于可嵌入对象。
@OneToMany@JoinColumn(name="A_ID")private List<C> cList;
如果您对embeddable中指定的列的A_ID名称不满意,则可以覆盖实体A中的列名称:
@AssociationOverride(name= "cList", joinColumns = @JoinColumn(name="SOME_NAME_FOR_JOIN_COLUMN_IN_TABLE_C"))@Embeddedprivate B b;
@ ManyToMany / @ OneToMany mappingby属性用于双向关联
我正在为我的实习工作开发JPA兼容套件…该套件的一部分涉及测试极端情况的正确实现。
@ManyToMany
具有mapedBy属性。JPA指出:
字符串mappingBy-拥有关系的字段或属性。除非关系是单向的,否则为必需。
没有给出默认值-默认列为空。
给定双向@ManyToMany
-此示例来自JPA 2.0 JSR-317规范本身!
顾客
@ManyToMany
@JoinTable(name="CUST_PHONES")
public Set<PhoneNumber> getPhones() { return phones; }
电话号码
@ManyToMany(mappedBy="phones")
public Set<Customer> getCustomers() { return customers; }
尚未在@ManyToMany
of的中定义mapledBy属性Customer
!我不知道的双向映射有默认值吗?
我查看了类似的情况,发现: @OneToOne
-mappingBy是可选的,没有默认值
@OneToMany
-完全相同@ManyToMany
(mappedBy对于双向是可选的,没有默认值)
简而言之,我的问题是:
对于@ManyToMany
和@OneToMany
,应在关系的拥有方(Customer
例如)中将其放置在mappedBy属性中?
@OneToMany、@ManyToOne以及@ManyToMany讲解
一、一对多(@OneToMany)
1、单向一对多模型
假设通过一个客户实体可以获得多个地址信息。
对于一对多的实体关系而言,表结构有两种设计策略,分别是外键关联和表关联。
(1) 映射策略---外键关联
在数据库中表customer和表结构address定义,如下:
create table customer (
id int(20) not null auto_increment,
name varchar(100),
primary key(id)
)
create table address (
id int(20) not null auto_increment,
province varchar(50),
city varchar(50),
postcode varchar(50),
detail varchar(50),
customer_id int(20),
primary key (id)
)
注意此时外键定义在多的一方,也就是address表中。
此时,表customer映射为实体CustomerEO,代码如下:
@Entity
@Table (name="customer")
public class CustomerEO implements java.io.Serializable {
@OneToMany(cascade={ CascadeType.ALL })
@JoinColumn(name="customer_id")
private Collection<AddressEO> addresses = new ArrayList<AddressEO>();
...
}
注释@OneToMany的定义代码如下:
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface OneToMany {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default LAZY;
String mappedBy() default "";
}
使用时要注意一下几点问题:
a、targetEntity属性表示默认关联的实体类型。如果集合类中指定了具体类型了,不需要使用targetEntity.否则要指定targetEntity=AddressEO.class。
b、mappedBy属性用于标记当实体之间是双向时使用。
(2) 映射策略---表关联
在上面address表中去掉customer_id字段,在增加一个表ref_customer_address,如下:
--客户地址关系表
create table ref_customer_address (
customer_id int(20) not null,
address_id int(20) not null unique
)
此时表customer映射为CustomerEO实体,代码如下:
@Entity
@Table(name = "customer")
public class CustomerEO implements java.io.Serializable {
...
@OneToMany(cascade = { CascadeType.ALL })
@JoinTable(name="ref_customer_address",
joinColumns={ @JoinColumn(name="customer_id",referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="address_id",referencedColumnName="id")})
private Collection<AddressEO> addresses = new ArrayList<AddressEO>();
...
}
表关联@JoinTable,定义如下:
@Target({METHOD,FIELD})
public @interface JoinTable {
String name() default "";
String catalog() default "";
String schema() default "";
JoinColumn[] joinColumns() default {};
JoinColumn[] inverseJoinColumns() default {};
UniqueConstraint[] uniqueConstraints default {};
}
其中:
a、该标记和@Table相似,用于标注用于关联的表。
b、name属性为连接两张表的表名。默认的表名为:“表名1”+“-”+“表名2”,上面例子默认的表名为customer_address。
c、joinColumns属性表示,在保存关系中的表中,所保存关联的外键字段。
d、inverseJoinColumns属性与joinColumns属性类似,不过它保存的是保存关系的另一个外键字段。
(3) 默认关联
在数据库底层为两张表添加约束,如下:
create table customer_address (
customer_id int(20) not null,
address_id int(20) not null unique
)
alter table customer_address add constraint fk_ref_customer foreign key (customer_id) references customer (id);
alter table customer_address add constraint fk_ref_address foreign key (address_id) references address (id);
这样,在CustomerEO中只需要在标注@OneToMany即可!
二、多对一@ManyToOne
1、单向多对一模型。
(1) 外键关联
配置AddressEO实体如下:
@Entity
@Table(name="address")
public class AddressEO implements java.io.Serializable {
@ManyToOne(cascade = { CascadeType.ALL })
@JoinColumn(name="customer_id")
private CustomerEO customer;
// ...
}
@ManyToOne定义如下:
@Target({METHOD,FIELD}) @Retention(RUNTIME)
public @interface ManyToOne {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fatch() default EAGER;
boolean optional() default true;
}
(2) 默认关联
数据库脚本定义的相关字段的约束,创建外键后,直接使用@ManyToOne
三、高级一对多和多对一映射
即双向关联模型,确定了双向关联后,多的一方AddressEO不变使用@ManyToOne,而CustomerEO实体修改为:
@Entity
@Table(name="customer")
public class CustomerEO {
@OneToMany(mappedBy="customer")
private Collection<AddressEO> addresses = new ArrayList<AddressEO>();
// ...
}
其中,@OneToMany标记中的mappedBy属性的值为AddressEO实体中所引用的CustomerEO实体的属性名。
四、多对多(@ManyToMany)
和一对多类型,不在赘述。@ManyToMany标记的定义如下:
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface ManyToMany {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fecth() default LAZY;
String mappedBy() default "";
}
五、最后,谈谈关于集合类的选择
在映射关系中可以使用的集合类有Collection、Set、List和Map,下面看下如何选择。
1、定义时使用接口,初始化使用具体的类。
如Collection可以初始化为ArrayList或HashSet;
Set可以初始化为HashSet;
List可以初始化为ArrayList;
Map可以初始化为HashMap.
2、集合类的选择
Collection类是Set和List的父类,在未确定使用Set或List时可使用;
Set集合中对象不能重复,并且是无序的;
List集合中的对象可以有重复,并且可以有排序;
Map集合是带有key和value值的集合。
@OneToMany、@ManyToOne以及@ManyToMany讲解(五)
一、一对多(@OneToMany)
1、单向一对多模型
假设通过一个客户实体可以获得多个地址信息。
对于一对多的实体关系而言,表结构有两种设计策略,分别是外键关联和表关联。
(1) 映射策略---外键关联
在数据库中表customer和表结构address定义,如下:
create table customer (
id int(20) not null auto_increment,
name varchar(100),
primary key(id)
)
create table address (
id int(20) not null auto_increment,
province varchar(50),
city varchar(50),
postcode varchar(50),
detail varchar(50),
customer_id int(20),
primary key (id)
)
注意此时外键定义在多的一方,也就是address表中。
此时,表customer映射为实体CustomerEO,代码如下:
@Entity
@Table(name="customer")
public class CustomerEO implements java.io.Serializable {
@OneToMany(cascade={ CascadeType.ALL })
@JoinColumn(name="customer_id")
private Collection<AddressEO> addresses = new ArrayList<AddressEO>();
...
}
注释@OneToMany的定义代码如下:
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface OneToMany {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default LAZY;
String mappedBy() default "";
}
使用时要注意一下几点问题:
a、targetEntity属性表示默认关联的实体类型。如果集合类中指定了具体类型了,不需要使用targetEntity.否则要指定targetEntity=AddressEO.class。
b、mappedBy属性用于标记当实体之间是双向时使用。
(2) 映射策略---表关联
在上面address表中去掉customer_id字段,在增加一个表ref_customer_address,如下:
--客户地址关系表
create table ref_customer_address (
customer_id int(20) not null,
address_id int(20) not null unique
)
此时表customer映射为CustomerEO实体,代码如下:
@Entity
@Table(name = "customer")
public class CustomerEO implements java.io.Serializable {
...
@OneToMany(cascade = { CascadeType.ALL })
@JoinTable(name="ref_customer_address",
joinColumns={ @JoinColumn(name="customer_id",referencedColumnName="id")},
inverseJoinColumns={@JoinColumn(name="address_id",referencedColumnName="id")})
private Collection<AddressEO> addresses = new ArrayList<AddressEO>();
...
}
表关联@JoinTable,定义如下:
@Target({METHOD,FIELD})
public @interface JoinTable {
String name() default "";
String catalog() default "";
String schema() default "";
JoinColumn[] joinColumns() default {};
JoinColumn[] inverseJoinColumns() default {};
UniqueConstraint[] uniqueConstraints default {};
}
其中:
a、该标记和@Table相似,用于标注用于关联的表。
b、name属性为连接两张表的表名。默认的表名为:“表名1”+“-”+“表名2”,上面例子默认的表名为customer_address。
c、joinColumns属性表示,在保存关系中的表中,所保存关联的外键字段。
d、inverseJoinColumns属性与joinColumns属性类似,不过它保存的是保存关系的另一个外键字段。
(3) 默认关联
在数据库底层为两张表添加约束,如下:
create table customer_address (
customer_id int(20) not null,
address_id int(20) not null unique
)
alter table customer_address add constraint fk_ref_customer foreign key (customer_id) references customer (id);
alter table customer_address add constraint fk_ref_address foreign key (address_id) references address (id);
这样,在CustomerEO中只需要在标注@OneToMany即可!
二、多对一@ManyToOne
1、单向多对一模型。
(1) 外键关联
配置AddressEO实体如下:
@Entity
@Table(name="address")
public class AddressEO implements java.io.Serializable {
@ManyToOne(cascade = { CascadeType.ALL })
@JoinColumn(name="customer_id")
private CustomerEO customer;
// ...
}
@ManyToOne定义如下:
@Target({METHOD,FIELD}) @Retention(RUNTIME)
public @interface ManyToOne {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fatch() default EAGER;
boolean optional() default true;
}
(2) 默认关联
数据库脚本定义的相关字段的约束,创建外键后,直接使用@ManyToOne
三、高级一对多和多对一映射
即双向关联模型,确定了双向关联后,多的一方AddressEO不变使用@ManyToOne,而CustomerEO实体修改为:
@Entity
@Table(name="customer")
public class CustomerEO {
@OneToMany(mappedBy="customer")
private Collection<AddressEO> addresses = new ArrayList<AddressEO>();
// ...
}
其中,@OneToMany标记中的mappedBy属性的值为AddressEO实体中所引用的CustomerEO实体的属性名。
四、多对多(@ManyToMany)
和一对多类型,不在赘述。@ManyToMany标记的定义如下:
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface ManyToMany {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fecth() default LAZY;
String mappedBy() default "";
}
五、最后,谈谈关于集合类的选择
在映射关系中可以使用的集合类有Collection、Set、List和Map,下面看下如何选择。
1、定义时使用接口,初始化使用具体的类。
如Collection可以初始化为ArrayList或HashSet;
Set可以初始化为HashSet;
List可以初始化为ArrayList;
Map可以初始化为HashMap.
2、集合类的选择
Collection类是Set和List的父类,在未确定使用Set或List时可使用;
Set集合中对象不能重复,并且是无序的;
List集合中的对象可以有重复,并且可以有排序;
Map集合是带有key和value值的集合。
Django笔记七之ManyToMany和OneToOne介绍
本文首发于微信公众号:Hunter后端
原文链接:Django笔记七之ManyToMany和OneToOne介绍
ManyToMany 是一种多对多的关系,在用途和使用方法上和外键 ForeignKey 类似。
以下是本篇笔记的目录:
- ManyToMany 的介绍
- through 参数
- through_fields 参数
- ManyToMany关系数据的增删改查
- OneToOne介绍
1、ManyToMany 的介绍
假设有两个 model,Person 和 Group,这两个model之间是多对多的关系。那么我们可以如下创建其关系:
# blog/models.py
class Person(models.Model):
name = models.CharField(max_length=64)
class Group(models.Model):
name = models.CharField(max_length=64)
members = models.ManyToManyField(Person)
通过上述代码我们就创建了有多对多关系两个model,当我们执行 migrate 操作之后(可以先不执行,后续还会对其有所更改),系统除了会创建 Person 和 Group 这两个表之外,还会创建一个表。
表名为 blog_group_members,因为是放在 Blog 这个 application 下面,所以,表名的前缀是 blog,然后加上 model 名的小写为 group,加上字段名称 members。
这张第三方表会有两个字段,person_id 和 group_id,将这两个 model 关联起来。
通过往这张第三方表写入 person_id 和 group_id的数据,我们就将这两个 model 关联了起来。
而获取他们对应的关系的记录和 ForeignKey 的关系类似:
根据 Person 数据查询关联的 Group 数据:
person = Person.objects.get(id=1)
group_list = person.group_set.all() # 使用 Group 的小写加 _set
根据 Group 数据查询关联的 Person 数据,这个查询方法略微不同,用到的是 Group 定义的 members 字段:
group = Group.objects.get(id=1)
person = group.members.all()
# 根据条件来搜索 person 也是可以的
person = group.members.filter(name=''hunter'')
2、through参数
上面 ManyToMany 的定义中,我们没有加任何参数,所以自动创建的表名是默认的,字段也只是两个 model 的主键id。
而如果我们有一些额外的需求,比如说,为 Person 和 Group 添加关联关系时,需要加上关联时间,或者想自己指定表名或 model 名的时候,我们可以通过 through 属性来指定 model 的名称,然后为其添加我们需要的字段。
比如我们想为 Person 和 Group 创建一个多对多的关系,指定model 名为 Membership,且额外添加字段,比如添加时间,可以通过 through 参数来指定:
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through=''Membership'',
)
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
3、through_fileds参数
在我们上面创建的 Membership model 中,我们对应的多对多的字段分别是 person 和 group,所以系统可以自动找到对应的多对多的字段。
如果在第三方表,也就是 Membership 中,有多个相同的 Person 或者 Group 的字段,就需要通过 through_fields 参数来指定多对多的字段:
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through=''Membership'',
through_fields=(''group'', ''person''),
)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
inviter = models.ForeignKey(
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
)
invite_reason = models.CharField(max_length=64)
4、ManyToMany关系数据的增删改查
接下来,我们定下最终的几个 model 内容如下,用于演示 ManyToMany 的增删改查的操作:
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through=''Membership'',
)
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
现在我们有 Person 和 Group 两个model,还有两个 model 之间的关系表 Membership,如果我们要创建一个对应的关系,则需要创建一个 Membership 实例。
创建
首先创建 Person 和 Group 的记录:
from blog.models import Person, Group, Membership
hunter = Person.objects.create(name=''hunter'')
group_1 = Group.objects.create(name=''group_1'')
创建多对多记录:
m1 = Membership.objects.create(person=hunter, group=group_1, date_joined=''2022-01-01'', invite_reason=''xxx'')
根据单个 Person 记录获取所有相关 Group 记录,使用方法同外键搜索方法:
groups = hunter.group_set.all()
根据单个 Group 记录获取所有相关 Person 记录,根据多对多字段来搜索:
persons = group_1.members.all()
根据 Membership 关系记录获取 Person 和 Group 记录,可以直接用外键的的方式使用:
m1.person
m1.group
根据 Group 使用 add 添加一条多对多记录:
paul = Person.objects.create(name=''pual'')
group_1.members.add(paul, through_defaults={''date_joined'': ''2022-01-01''})
其中,through_defaults 参数为字典,内容为多对多关系表的额外添加的字段。
根据 Group 使用 create 创建一条多对多记录:
如果没有相应的 Person 记录,可以根据 Group 来直接创建
group_1.members.create(name=''mary'', through_defaults={''date_joined'': ''2022-01-01''})
根据 Group 使用 set 刷新多对多记录:
使用 set 方法来设置多对多的关系:
jack = Person.objects.create(name=''jack'')
lucy = Person.objects.create(name=''lucy'')
group_1.members.set([jack, lucy], through_defaults={''date_joined'': ''2022-01-01''})
需要注意的是,使用 set() 方法加上关联之后,这个 Group 实例之前设置的关联数据都会被清除。
也就是说,set() 里设置的关联数据就是最终所有的关联数据。
根据 Group 使用 remove 删除一条多对多记录:
在上面 我们使用了 set() 方法设置了两条关联数据,jack 和 lucy,现在我们想要把 jack——group_1 这条关系删除,可使用 remove() 方法:
group_1.members.remove(jack)
使用 clear 清除某个 Group 实例上所有关系:
group_1.members.clear()
多对多的搜索:
根据 Person 的条件搜索 Group 的数据:
比如搜索 Person 的 name 字段为 ''hunter'' 的 Group 数据
Group.objects.filter(members__name=''hunter'')
根据 Group 的条件搜索 Person 的数据:
比如搜索 Group 的 name 字段为 ''group_1'' 的Person关联数据:
Person.objects.filter(group__name=''group_1'')
如果要搜索额外的关联字段:
Person.objects.filter(group__name=''group_1'', membership__date_joined=''2022-01-01'')
5、OneToOne 介绍
不同于 多对一和多对多的关系,OneToOne 是一对一的关系,也就是说 一条数据仅能被另一条数据关联。
下面是两个 OneToOne 对应的 model:
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
def __str__(self):
return "%s the place" % self.name
class Restaurant(models.Model):
place = models.OneToOneField(Place, on_delete=models.CASCADE, default=None, related_name=''place_restaurant'', null=True)
接下来创建两条数据:
r_1 = Restaurant.objects.create()
p_1 = Place.objects.create(name=''beijing'', address=''beijing'')
根据 Restaurant 获取 Place 数据,直接根据字段获取数据:
r_1.place
如果根据 Place 获取 Restaurant 数据,因为是 OneToOne 的关系,所以可以直接获取:
上面的 model 中我们定义了 related_name,所以是:
p_1.place_restaurant
如果没有定义 related_name 和 related_query_name 那么就是 model 的小写:
p_1.restaurant
但是从 Place 到 Restaurant 获取数据,如果没有这种 OneToOne 的对应,比如我们上面直接创建的 p_1,使用这种方式获取关联数据就会报错,因为没有这种 OneToOne 的数据。
那么这个时候我们可以先判断是否有对应的这种 OneToOne 的数据:
hasattr(p_1, ''place_restaurant'') # 返回的是布尔型数据
# 或者
getattr(p_1, ''place_restaurant'', None)
以上就是这篇笔记全部内容,接下来将要介绍 model 里的 Meta 的用法。
如果想获取更多相关文章,可扫码关注阅读:
我们今天的关于具有@OneToMany属性的可嵌入实体的分享就到这里,谢谢您的阅读,如果想了解更多关于@ ManyToMany / @ OneToMany mappingby属性用于双向关联、@OneToMany、@ManyToOne以及@ManyToMany讲解、@OneToMany、@ManyToOne以及@ManyToMany讲解(五)、Django笔记七之ManyToMany和OneToOne介绍的相关信息,可以在本站进行搜索。
本文标签: