图源:
这篇文章会介绍@Embedded
和@Embeddable
两个注解在 JPA 中的用法。
简单示例
先看一个示例:
name = "user_student")
(chain = true)
(onlyExplicitlyIncluded = true)
(public class Student {
strategy = GenerationType.AUTO)
( Include
. private Long id;
length = 25, nullable = false)
( private String name;
length = 50, nullable = false)
( private String address;
length = 25)
( private String contactsName;
length = 50)
( private String contactsAddress;
length = 15)
( private String contactsPhone;
public Student() {
}
}
这里使用了 Lombok 相关注解(比如
@Builder
)帮助构建实体类,详细内容可以阅读我的。
user_student
是一个学生表,其中的contacts_
开头的字段保存联系人信息,这体现在实体类中就是以contacts
开头的属性。
测试用例:
void testAddNewStudent() {
Student newStudent = Student.builder()
.address("宁安大街101号")
.name("icexmoon")
.contactsName("lalala")
.contactsAddress("北京大街100号")
.contactsPhone("123456789")
.build();
studentRepository.save(newStudent);
Assertions.assertNotNull(newStudent.getId());
ObjectMapper om = new ObjectMapper();
var json = om.writeValueAsString(newStudent);
System.out.println(json);
}
这样做并没有什么问题,但Student
这个实体类并不具备良好的“结构化”,换言之我们很难将其中的联系人部分进行代码重用。
因此,接下来我们要想办法将Student
中的联系人部分信息提取出来单独作为一个类型存在,这可以借助 JPA 的@Embedded
和@Embeddable
注解完成。
@Embedded 和 @Embeddable
先定义一个联系人类:
public class Contacts {
private String name;
private String address;
private String phone;
}
使用@Embedded
和@Embeddable
“改造”Student
类:
name = "user_student2")
(chain = true)
(onlyExplicitlyIncluded = true)
(public class Student2 {
strategy = GenerationType.AUTO)
( Include
. private Long id;
length = 25, nullable = false)
( private String name;
length = 50, nullable = false)
( private String address;
({ name = "name", column = (name = "contacts_name", length = 25)),
( name = "address", column = (name = "contacts_address", length = 50)),
( name = "phone", column = (name = "contacts_phone", length = 15))
( })
private Contacts contacts;
public Student2() {
}
}
这里的@Embeddable
注解表明该类可以被“嵌入”到一个实体类中,充当某些字段的映射。@Embedded
注解表明这里嵌入了一个用@Embeddable
标记的类。
就像以前学习 MyBastis 时在一个 MapperSet 中嵌入另一个 MapperSet 时需要指定字段映射关系,这里同样需要指定,这体现在 @AttributeOverrides
注解中包含的多条@AttributeOverride
注解。其name
属性表示的是被嵌入的类型的属性名称,column
属性表示的是对应的数据库表结构中的字段信息。
如果缺省
@AttributeOverrides
和@AttributeOverride
注解,默认会用被嵌入的类型(这里是Contacts
)的属性名称作为表结构字段名进行映射。但显然这里是行不通的,会报错(因为联系人的姓名与学生的姓名都会映射到同一个name
字段)。
现在实体类变得更加“结构化”,这点在测试用例中构建新对象时体现的很明显:
void testAddNewStudent() {
Student2 newStudent = Student2.builder()
.address("宁安大街101号")
.name("icexmoon")
.contacts(Contacts.builder()
.name("lalala")
.address("北京东路100号")
.phone("123456789")
.build())
.build();
student2Repository.save(newStudent);
Assertions.assertNotNull(newStudent.getId());
ObjectMapper om = new ObjectMapper();
var json = om.writeValueAsString(newStudent);
System.out.println(json);
}
输出的 JSON 串也能更清楚地观察到结构化的好处。
除了上述的典型用途之外,还可以用@Embedded
标记的类定义联合主键,具体可以查看。
The End,谢谢阅读。
本文的完整示例代码可以从获取。
文章评论