一对多关系是关系数据库中两个表之间的一种关系,该关系中第一个表中的单个行可以与第二个表中的一个或多个行相关,但第二个表中的一个行只可以与第一个表中的一个行相关。
下面将通过用户和用户联系方式为例,来介绍 OpenJPA 中一对多关系映射。
一对多关系的表结构设计通常和单向多对一是一致的,也可以在多方添加外键列来维护两者之间的关系。但是,OpenJPA 中使用的是中间表来维护关系。这样在性能方面来说,没有外键列这种设计方式好。数据表结构图如下:
(1)用户表对应的实体对象 User,代码如下:
@Data @Entity @Table public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; @Column private String name; @Column private Integer age; // 关键代码 @OneToMany(cascade={ CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }, orphanRemoval = true) private Collection<Contact> contacts; }
(2)用户联系信息表对应的 Contact 实体,代码如下:
@Data @Entity @Table public class Contact { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column private Integer id; @Column private String email; @Column private String homeUrl; public Contact() {} public Contact(String email, String homeUrl) { this.email = email; this.homeUrl = homeUrl; } }
(1)修改 persistence.xml 配置文件,如下:
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.0"> <persistence-unit name="demo_onetomany" transaction-type="RESOURCE_LOCAL"> <!-- JPA提供者 --> <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider> <!-- 声明实体类 --> <class>com.hxstrive.openjpa.annotation.demo_onetomany.User</class> <class>com.hxstrive.openjpa.annotation.demo_onetomany.Contact</class> <!-- 配置JPA数据库属性 --> <properties> <property name="openjpa.ConnectionURL" value="jdbc:mysql://localhost:3306/openjpa_learn?useSSL=false& serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8"/> <property name="openjpa.ConnectionDriverName" value="com.mysql.jdbc.Driver"/> <property name="openjpa.ConnectionUserName" value="root"/> <property name="openjpa.ConnectionPassword" value="aaaaaa"/> <property name="openjpa.Log" value="SQL=TRACE"/> <!-- 自动生成表 --> <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/> <!-- 不使用加载时强化和编译时强化,使用运行时Unenhanced(不能发挥OpenJPA的最大效能,所以也不推荐) --> <property name="openjpa.ClassLoadEnhancement" value="false"/> <property name="openjpa.DynamicEnhancementAgent" value="false"/> <property name="openjpa.RuntimeUnenhancedClasses" value="supported"/> </properties> </persistence-unit> </persistence>
(2)用户实体对象 User,代码如下:
@Data @Entity @Table public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; @Column private String name; @Column private Integer age; // 关键代码 @OneToMany(cascade={ CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }, orphanRemoval = true) private Collection<Contact> contacts; }
(3)用户联系信息对象 Contact,代码如下:
@Data @Entity @Table public class Contact { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column private Integer id; @Column private String email; @Column private String homeUrl; public Contact() {} public Contact(String email, String homeUrl) { this.email = email; this.homeUrl = homeUrl; } }
(4)客户端代码
public class Demo { /** 持久化单元名称 */ private static final String NAME = "demo_onetomany"; public static void main(String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory(NAME); EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); // 插入数据 String uuid = UUID.randomUUID().toString(); List<Contact> userExts = new ArrayList<Contact>(); userExts.add(new Contact(uuid + "@qq.com", "http://www.hxstrive1.com")); userExts.add(new Contact(uuid + "@163.com", "http://www.hxstrive2.com")); userExts.add(new Contact(uuid + "@outlook.com", "http://www.hxstrive3.com")); User user = new User(); user.setName("张三-" + uuid); user.setAge(28); user.setContacts(userExts); em.persist(user); // 查询数据 String qlString = "select t from User t"; Query query = em.createQuery(qlString); List<User> userList = (List<User>) query.getResultList(); for (User item : userList) { System.out.println(JSONObject.toJSONString(item)); } em.getTransaction().commit(); em.close(); emf.close(); System.out.println("finished."); } }
运行上面客户端代码,将依次执行下面SQL语句:
INSERT INTO User (id, age, name) VALUES (?, ?, ?) [params=?, ?, ?] INSERT INTO Contact (id, email, homeUrl, user16_id) VALUES (?, ?, ?, ?) [params=?, ?, ?, ?] INSERT INTO Contact (id, email, homeUrl, user16_id) VALUES (?, ?, ?, ?) [params=?, ?, ?, ?] INSERT INTO Contact (id, email, homeUrl, user16_id) VALUES (?, ?, ?, ?) [params=?, ?, ?, ?] INSERT INTO User_Contact (USER_ID, CONTACTS_ID) VALUES (?, ?) [params=?, ?] INSERT INTO User_Contact (USER_ID, CONTACTS_ID) VALUES (?, ?) [params=?, ?] INSERT INTO User_Contact (USER_ID, CONTACTS_ID) VALUES (?, ?) [params=?, ?] SELECT t0.id, t0.age, t0.name FROM User t0
执行客户端代码后,数据库插入数据如下图: