单向一对多映射

一对多关系是关系数据库中两个表之间的一种关系,该关系中第一个表中的单个行可以与第二个表中的一个或多个行相关,但第二个表中的一个行只可以与第一个表中的一个行相关。

下面将通过用户和用户联系方式为例,来介绍 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&amp;
                      serverTimezone=UTC&amp;useUnicode=true&amp;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

执行客户端代码后,数据库插入数据如下图:

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号