JPA框架-程序员宅基地

技术标签: spring  java  spring框架  hibernate  

目录

一、JPA 介绍

1、JDBC

2、JPA是什么

二、搭建 JPA 环境

三、JPA 注解

四、JPA API

1、缓存

2、EntityManager

3、API

五、关联关系映射

1、一对一映射

2、单向一对多

3、单向多对一

4、双向一对多及多对一

5、双向多对多

六、JPQL

1、createQuery

2、createNativeQuery


一、JPA 介绍

1、JDBC

2、JPA是什么

  1. Java Persistence API:Java对象持久化API

  2. JDK5.0 平台的标准 ORM 规范,可以让 Java 程序用统一方式访问持久层

  3. JPA 和 Hibernate 的关系

    • JPA 是 Hibernate 的一个抽象(JDBC Interface 与 JDBC驱动的关系)

    • JPA 本质上就是一种 ORM 规范,不是 ORM 框架,因为 JPA 并未提供 ORM 实现,它只提供了 API 接口,具体的实现由 ORM 厂商提供实现

    • Hibernate 是 一个 ORM 框架,同时也是一种 JPA 的实现

    • Hibernate 从3.2版本开始兼容 JPA

    • Hibernate 使用起来是XML配置文件的方式,而 JPA 是注解的方式,而注解是JDK5.0中自带的,所以并不需要再引入第三方Jar包,实际上就是来学习如何使用注解的方式来使用 Hibernate

  4. 要使用到 JPA 的哪些技术

    • ORM 映射元数据:JPA 支持 XML 和 JDK5.0 注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架将实体对象持久化到数据库表中

    • JPA 的 API:利用实体对象,操作 JPA 提供的接口进行 CRUD 操作,简化开发者编程代码

    • 查询语言(JPQL):这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言去查询数据

二、搭建 JPA 环境

  1. 使用Maven构建项目,项目名为:jpa

  2. 配置pom.xml

    <properties>
        <spring.version>5.2.6.RELEASE</spring.version>
        <hibernate.version>5.4.10.Final</hibernate.version>
        <mysql.version>8.0.21</mysql.version>
        <ehcache.version>3.8.1</ehcache.version>
        <jpa.version>1.0.1.Final</jpa.version>
        <slf4j.version>1.7.25</slf4j.version>
        <aspectj.version>1.9.5</aspectj.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <dependencies>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!-- Hibernate -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-hikaricp</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-ehcache</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.0-api</artifactId>
            <version>${jpa.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>org.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>${ehcache.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
  3. 创建 com.javakc.jpa 包

  4. 创建 entity 包并在其目录下创建实体类

  5. 创建 dao 包并在其目录下创建数据层实现类

  6. 在 resources 目录下创建 jdbc.properties 配置文件

    jdbc.driverClass=com.mysql.cj.jdbc.Driver
    jdbc.jdbcUrl=jdbc:mysql:///jpa?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    jdbc.user=root
    jdbc.password=123456

  7. 在 resources 目录下创建 spring-jpa.xml 配置文件 
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    
        <!-- 配置自动扫描的包 -->
        <context:component-scan base-package="com.javakc.jpa"></context:component-scan>
    
        <!-- 加载配置文件 -->
        <context:property-placeholder location="jdbc.properties"></context:property-placeholder>
    
        <!-- 配置 Hikari 数据源 -->
        <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
            <property name="driverClassName" value="${jdbc.driverClass}"></property>
            <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
            <property name="username" value="${jdbc.user}"></property>
            <property name="password" value="${jdbc.password}"></property>
        </bean>
    
        <!-- 配置 EntityManagerFactory -->
        <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
            <property name="dataSource" ref="dataSource"></property>
            <!-- JPA 提供商的适配器 -->
            <property name="jpaVendorAdapter">
                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
            </property>
            <!-- 配置实体类所在的包 -->
            <property name="packagesToScan" value="com.javakc.jpa.entity"></property>
            <!-- 配置 JPA 的基本属性 -->
            <property name="jpaProperties">
                <props>
                    <prop key="hibernate.show_sql">true</prop>
                    <prop key="hibernate.format_sql">true</prop>
                    <prop key="hibernate.hbm2ddl.auto">update</prop>
                </props>
            </property>
        </bean>
    
        <!-- 配置 JPA 使用的事物管理器 -->
        <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
            <property name="entityManagerFactory" ref="entityManagerFactory"></property>
        </bean>
    
        <!-- 配置支持基于注解的事物配置 -->
        <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    
    </beans>
  8. 创建测试类

    import com.javakc.jpa.dao.JpaDao;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:spring-jpa.xml"})
    public class JpaTest {
    
        @Autowired
        private JpaDao jpaDao;
    
        @Test
        public void test() {
        }
    
    }
     

三、JPA 注解

  1. @Entity

    • 标注用于实体类声明语句之前,指出该Java 类为实体类,将映射到指定的数据库表

  2. @Table

    • 当实体类与其映射的数据库表名不同名时需要使用 @Table 标注说明,该标注与 @Entity 标注并列使用,置于实体类声明语句之前,可写于单独语句行,也可与声明语句同行

    • @Table 标注的常用选项是 name,用于指明数据库的表名

    • @Table标注还有一个两个选项 catalog 和 schema 用于设置表所属的数据库目录或模式,通常为数据库名。uniqueConstraints 选项用于设置约束条件,通常不须设置

  3. @Id

    • 标注用于声明一个实体类的属性映射为数据库的主键列。该属性通常置于属性声明语句之前

  4. @GeneratedValue

    1. 用于标注主键的生成策略,通过 strategy 属性指定。默认情况下,JPA 自动选择一个最适合底层数据库的主键生成策略

    2. 在 javax.persistence.GenerationType 中定义了以下几种可供选择的策略:

      • IDENTITY:采用数据库 ID自增长的方式来自增主键字段,Oracle 不支持这种方式

      • AUTO: JPA自动选择合适的策略,是默认选项

      • SEQUENCE:通过序列产生主键,通过 @SequenceGenerator 注解指定序列名,MySql 不支持这种方式

      • TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植

  5. @Column

    • 当实体的属性与其映射的数据库表的列不同名时需要使用@Column 标注说明,该属性通常置于实体的属性声明语句之前,还可与 @Id 标注一起使用

    • 常用属性是 name,用于设置映射数据库表的列名。此外,该标注还包含其它多个属性,如:unique 、nullable、length 等

  6. @Transient

    • 表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性

    • 如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则,ORM框架默认其注解为@Basic

  7. @Temporal

    • 在核心的 Java API 中并没有定义 Date 类型的精度(temporal precision). 而在数据库中,表示 Date 类型的数据有 DATE, TIME, 和 TIMESTAMP 三种精度(即单纯的日期,时间,或者两者 兼备). 在进行属性映射时可使用 @Temporal 注解来调整精度

四、JPA API

1、缓存

  • 一级缓存:会话级别,对同一个id进行两次加载,不会发送两条sql给数据库,但会话关闭,一级缓存就会失效

  • 二级缓存:全局级别,一级缓存会话关闭,缓存也不会失效

2、EntityManager

  1. 在 JPA 规范中, EntityManager 是完成持久化操作的核心对象。实体作为普通 Java 对象,只有在调用 EntityManager 将其持久化后才会变成持久化对象。EntityManager 对象在一组实体类与底层数据源之间进行 O/R 映射的管理。它可以用来管理和更新 Entity Bean, 根椐主键查找 Entity Bean, 还可以通过JPQL语句查询实体。

  2. 实体的状态:

    • 临时状态: 新创建的对象,尚未拥有持久性主键,不处于缓存中,数据库中也没有对应的记录

    • 持久化状态:已经拥有持久性主键,位于缓存中,数据库中有对应记录

    • 游离状态:拥有持久化主键,不处于缓存中,数据库中可能存在对应的记录

    • 删除状态: 曾经位于缓存中,曾在数据库中有过记录,但现在已被删除

3、API

  1. persist

    • 用于将新创建的 Entity 纳入到 EntityManager 的管理。该方法执行后,传入 persist() 方法的 Entity 对象转换成持久化状态

    • 如果传入 persist() 方法的 Entity 对象已经处于持久化状态,则 persist() 方法什么都不做

    • 如果对删除状态的 Entity 进行 persist() 操作,会转换为持久化状态

    • 如果对游离状态的实体执行 persist() 操作,可能会在 persist() 方法抛出 EntityExistException(也有可能是在flush或事务提交后抛出)

    public void persist() {
        Student student = new Student();
        student.setStudentName("AA");
        student.setBirthday(new Date());
        student.setCreateDate(new Date());
        entityManager.persist(student);
        System.out.println(student.getId());
    }
  2. find

    • 返回指定的 OID 对应的实体类对象,如果这个实体存在于当前的持久化环境,则返回一个被缓存的对象;否则会创建一个新的 Entity, 并加载数据库中相关信息;若 OID 不存在于数据库中,则返回一个 null。第一个参数为被查询的实体类类型,第二个参数为待查找实体的主键值

    public void find() {
        Student student = entityManager.find(Student.class, 1);
        Student student2 = entityManager.find(Student.class, 1);
        System.out.println("-----------");
        System.out.println(student);
        System.out.println(student2);
    }
  3. getReference

    • 与find()方法类似,不同的是:如果缓存中不存在指定的 Entity, EntityManager 会创建一个 Entity 类的代理,但是不会立即加载数据库中的信息,只有第一次真正使用此 Entity 的属性才加载,所以如果此 OID 在数据库不存在,getReference() 不会返回 null 值, 而是抛出EntityNotFoundException

    public void getReference() {
        Student student = entityManager.getReference(Student.class, 1);
        System.out.println("-----------");
        System.out.println(student);
    }
  4. remove

    • 删除实例。如果实例是被管理的,即与数据库实体记录关联,则同时会删除关联的数据库记录

    public void remove() {
        Student student = entityManager.find(Student.class, 1);
        entityManager.remove(student);
    }
  5. merge:merge() 用于处理 Entity 的同步。即数据库的插入和更新操作

    1. 创建一个新的对象,把临时对象的属性复制到新的对象中,然后对新的对象执行持久化操作。所以新的对象中有id,但以前的临时对象中没有id

      public void merge1() {
          Student student = new Student();
          student.setStudentName("AA");
          student.setBirthday(new Date());
          student.setCreateDate(new Date());
          Student student2 = entityManager.merge(student);
          System.out.println("student#id=" + student.getId());
          System.out.println("student2#id=" + student2.getId());
      }
    2. 传入一个游离对象, 即传入的对象有 OID,如果在 EntityManager 缓存中没有该对象,如果在数据库中没有对应的记录,JPA 会创建一个新的对象, 然后把当前游离对象的属性复制到新创建的对象中,对新创建的对象执行 insert 操作

      public void merge2() {
          Student student = new Student();
          student.setStudentName("AA");
          student.setBirthday(new Date());
          student.setCreateDate(new Date());
      
          student.setId(100);
          Student student2 = entityManager.merge(student);
          System.out.println("student#id=" + student.getId());
          System.out.println("student2#id=" + student2.getId());
      }
    3. 传入一个游离对象, 即传入的对象有 OID,如果在 EntityManager 缓存中没有该对象,如果在数据库中有对应的记录,JPA 查询到对应的记录, 返回查询对象, 再然后会把游离对象的属性复制到查询到的对象中,对查询到的对象执行 update 操作

      public void merge3() {
          Student student = new Student();
          student.setStudentName("BB");
          student.setBirthday(new Date());
          student.setCreateDate(new Date());
      
          student.setId(1);
          entityManager.merge(student);
      }

五、关联关系映射

1、一对一映射

在双向的一对一关联中,需要在不维护关系端中的 @OneToOne 注解中指定 mappedBy,以指定是这一关联中的被维护端。同时需要在关系维护端建立外键列指向关系被维护端的主键列

  1. 设置关系

    // ## 使用 @OneToOne 来映射 1-1 关联关系
    // ## 在当前表中维护关系需要使用 @JoinColumn 来进行映射, 在 1-1 关联关系中需要添加 unique=true
    @OneToOne
    @JoinColumn(name = "student_id", unique = true)
    private Student student;
    // ## 当前表不维护关联关系,没有外键,使用 @OneToOne 来进行映射, 需要设置 mappedBy="student",如果两边都维护关联关系则会多出无用sql语句
    @OneToOne(mappedBy = "student")
    private Card card;
  2. 测试

    1. 保存

      /**
       * 双向 1-1 的关联关系,建议先去保存不维护外键的一方,这样不会多出 update 语句
       */
      public void oneToOnePersist() {
          Student student = new Student();
          student.setStudentName("BB");
      
          Card card = new Card();
          card.setCardNum("110");
      
          // ## 设置关联关系
          student.setCard(card);
          card.setStudent(student);
      
          // ## 保存
          entityManager.persist(student);
          entityManager.persist(card);
      
      }
    2. 获取1

      /**
       * 获取维护外键的一方,默认会使用左外连接获取其关联的对象
       * 可通过 @OneToOne 的 fetch 属性来修改加载策略(@OneToOne(fetch = FetchType.LAZY))
       */
      public void oneToOneFind() {
          Card card = entityManager.find(Card.class, 1);
          System.out.println(card.getCardNum());
          System.out.println(card.getStudent().getClass().getName());
      }
    3. 获取2

      /**
       * 获取不维护外键的一方,默认会使用左外连接获取其关联的对象
       * 可通过 @OneToOne 的 fetch 属性来修改加载策略,但依然会查询另一方,来初始化关联对象
       * 所以不建议修改加载策略
       */
      public void oneToOneFind2() {
          Student student = entityManager.find(Student.class, 10);
          System.out.println(student.getStudentName());
          System.out.println(student.getCard().getClass().getName());
      }

2、单向一对多

单向一对多关系中,在 1 端设置 @OneToMany 注解,并使用 @JoinColumn 指定外键列的名称

  1. 设置关系

    // ## 使用 @OneToMany 映射单向 1-n 关联关系
    // ## 使用 @JoinColumn 映射外键列的名称
    @OneToMany
    @JoinColumn(name = "classroom_id")
    private List<Student> studentList = new ArrayList<>();
  2. 测试

    1. 保存

      /**
       * 单向 1-n 关联关系保存时一定会多出 update 语句
       * 因为 n 端在插入数据时不会同时插入外键列需要用修改来补充外键值
       */
      public void oneToManyPersist() {
          ClassRoom classRoom = new ClassRoom();
          classRoom.setClassRoomName("javakc80");
      
          Student student1 = new Student();
          student1.setStudentName("CC");
          student1.setBirthday(new Date());
          student1.setCreateDate(new Date());
      
          Student student2 = new Student();
          student2.setStudentName("DD");
          student2.setBirthday(new Date());
          student2.setCreateDate(new Date());
      
          // ## 设置关联关系
          classRoom.getStudentList().add(student1);
          classRoom.getStudentList().add(student2);
      
          // ## 保存
          entityManager.persist(classRoom);
          entityManager.persist(student1);
          entityManager.persist(student2);
      }
    2. 获取

      /**
       * 默认为关联的 n 端使用懒加载策略
       * 可通过 @OneToMany 的 fetch 属性来修改加载策略关闭懒加载
       */
      public void oneToManyFind() {
          ClassRoom classRoom = entityManager.find(ClassRoom.class, 1);
          System.out.println(classRoom.getClassRoomName());
          System.out.println(classRoom.getStudentList().size());
      }
    3. 删除

      /**
       * 默认删除 1 端的数据前,先把关联的 n 端的外键置空,再删除 1 端数据
       * 可以通过 @OneToMany 的 cascade 属性来修改默认的删除策略
       */
      public void oneToManyRemove() {
          ClassRoom classRoom = entityManager.find(ClassRoom.class, 1);
          entityManager.remove(classRoom);
      }
    4. 修改

      public void oneToManyUpdate() {
          ClassRoom classRoom = entityManager.find(ClassRoom.class, 1);
          classRoom.getStudentList().get(0).setStudentName("FFF");
      }

3、单向多对一

单向多对一关系中,在 n 端设置 @ManyToOne 注解,并使用 @JoinColumn 指定外键名称

  1. 设置关系

    // ## 使用 @ManyToOne 映射单向 n-1 关联关系
    // ## 使用 @JoinColumn 映射外键
    @ManyToOne
    @JoinColumn(name = "classroom_id")
    private ClassRoom classRoom;
  2. 测试

    1. 保存

      /**
       * 先保存 1 的一端,后保存 n 的一端,这样不会多出额外的 update 语句
       */
      public void manyToOnePersist() {
          ClassRoom classRoom = new ClassRoom();
          classRoom.setClassRoomName("javakc80");
      
          Student student1 = new Student();
          student1.setStudentName("CC");
          student1.setBirthday(new Date());
          student1.setCreateDate(new Date());
      
          Student student2 = new Student();
          student2.setStudentName("DD");
          student2.setBirthday(new Date());
          student2.setCreateDate(new Date());
      
          // ## 设置关联关系
          student1.setClassRoom(classRoom);
          student2.setClassRoom(classRoom);
      
          // ## 保存
          entityManager.persist(classRoom);
          entityManager.persist(student1);
          entityManager.persist(student2);
      
      }
    2. 获取

      /**
       * 使用左外连接的方式获取 n 端的对象和其关联的 1 端的对象数据
       * 可通过 @ManyToOne 的 fetch 属性来修改加载策略
       */
      public void manyToOneFind() {
          Student student = entityManager.find(Student.class, 1);
          System.out.println(student.getStudentName());
          System.out.println(student.getClassRoom().getClassRoomName());
      }
    3. 删除1

      public void manyToOneRemove1() {
          Student student = entityManager.find(Student.class, 1);
          // ## 删除 n 端
          entityManager.remove(student);
      }
    4. 删除2

      public void manyToOneRemove2() {
          ClassRoom classRoom = entityManager.find(ClassRoom.class, 1);
          // ## 删除 1 端
          entityManager.remove(classRoom);
      }
    5. 修改

      public void manyToOneUpdate() {
          Student student = entityManager.find(Student.class, 1);
          student.getClassRoom().setClassRoomName("javakc");
      }

4、双向一对多及多对一

双向关系中,必须存在一个关系维护端,在 JPA 规范中,要求 many 的一方作为关系的维护端, one 的一端不维护关系。 可以在 one 方指定 @OneToMany 注解,并设置 mappedBy 属性,以指定它这一端不维护关联关系,many 为维护端。 在 many 方指定 @ManyToOne 注解,并使用 @JoinColumn 指定外键名称

  1. 设置关系

    // ## 使用 @ManyToOne 映射 n-1 关联关系
    // ## 使用 @JoinColumn 映射外键
    @ManyToOne
    @JoinColumn(name = "classroom_id")
    private ClassRoom classRoom;
    // ## 使用 @OneToMany 映射 1-n 关联关系
    // ## 使用 @JoinColumn 映射外键列的名称
    // ## 在 1 端的 @OneToMany 中使用 mappedBy 属性放弃维护关系, 就无需再使用 @JoinColumn 注解
    @OneToMany(mappedBy = "classRoom")
    private List<Student> studentList = new ArrayList<>();
  2. 测试

    1. 保存

      /**
       * 先保存 1 的一端,后保存 n 的一端,这样不会多出额外的 update 语句
       * 在双向关系下,保存 1 端会出现额外的 update 语句
       * 建议使用 n 端来维护关联关系,1 端不维护关联关系, 这样就会减少 update 语句
       * 注意: 在 1 端的 @OneToMany 中使用 mappedBy 属性, 就无需再使用 @JoinColumn 注解
       */
      public void manyToOnePersist() {
          ClassRoom classRoom = new ClassRoom();
          classRoom.setClassRoomName("javakc80");
      
          Student student1 = new Student();
          student1.setStudentName("CC");
          student1.setBirthday(new Date());
          student1.setCreateDate(new Date());
      
          Student student2 = new Student();
          student2.setStudentName("DD");
          student2.setBirthday(new Date());
          student2.setCreateDate(new Date());
      
          // ## 设置关联关系
          student1.setClassRoom(classRoom);
          student2.setClassRoom(classRoom);
      
          classRoom.getStudentList().add(student1);
          classRoom.getStudentList().add(student2);
      
          // ## 保存
          entityManager.persist(classRoom);
          entityManager.persist(student1);
          entityManager.persist(student2);
      
      }

5、双向多对多

在双向多对多关系中,我们必须指定一个关系维护端,可以通过 @ManyToMany 注解中指定 mappedBy 属性来标识放弃关系维护

  1. 设置关系

    // ## 使用 @ManyToMany 映射 n-n 关联关系
    // ## @JoinTable(name = "中间表名",
    // ## joinColumns = @JoinColumn(name = "本类的外键"),
    // ## inverseJoinColumns = @JoinColumn(name = "对方类的外键"))
    @ManyToMany
    @JoinTable(name = "jpa_course_student",
               joinColumns = @JoinColumn(name = "course_id"),
               inverseJoinColumns = @JoinColumn(name = "student_id"))
    private List<Student> studentList = new ArrayList<>();
    // ## 使用 @ManyToMany 映射 n-n 关联关系
    // ## 使用 mappedBy 属性放弃维护关系
    @ManyToMany(mappedBy = "studentList")
    private List<Course> courseList = new ArrayList<>();
  2. 测试

    1. 保存

      public void manyToManyPersist() {
          Course course1 = new Course();
          course1.setCourseName("钢琴");
      
          Course course2 = new Course();
          course2.setCourseName("美术");
      
          Student student1 = new Student();
          student1.setStudentName("小明");
      
          Student student2 = new Student();
          student2.setStudentName("小红");
      
          // ## 设置关联关系
          course1.getStudentList().add(student1);
          course1.getStudentList().add(student2);
          course2.getStudentList().add(student1);
          course2.getStudentList().add(student2);
      
          student1.getCourseList().add(course1);
          student1.getCourseList().add(course2);
          student2.getCourseList().add(course1);
          student2.getCourseList().add(course2);
      
          // ## 保存
          entityManager.persist(course1);
          entityManager.persist(course2);
          entityManager.persist(student1);
          entityManager.persist(student2);
      
      }
    2. 获取

      public void manyToManyFind() {
          Course course = entityManager.find(Course.class, 1);
          System.out.println(course.getCourseName());
          System.out.println(course.getStudentList().size());
      }

六、JPQL

JPQL语言,即 Java Persistence Query Language 的简称。JPQL 是一种和 SQL 非常类似的中间性和对象化查询语言,它最终会被编译成针对不同底层数据库的 SQL 查询,从而屏蔽不同数据库的差异

1、createQuery

public void query() {
    String jpql = "FROM Student";
    Query query = entityManager.createQuery(jpql);
    List<Student> list = query.getResultList();

    System.out.println(list.size());
}

2、createNativeQuery

public void nativeQuery() {
    String sql = "select * from jpa_student s";
    Query query = entityManager.createNativeQuery(sql);
    List list = query.getResultList();
    System.out.println(list.size());
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/CZW2181119177/article/details/126172117

智能推荐

wxWidgets:常用表达式_wxwidget 正则表达式 非数字字符-程序员宅基地

文章浏览阅读282次。wxWidgets:常用表达式wxWidgets:常用表达式不同风味的正则表达式转义Escapes元语法匹配限制和兼容性基本正则表达式正则表达式字符名称wxWidgets:常用表达式一个正则表达式描述字符的字符串。这是一种匹配某些字符串但不匹配其他字符串的模式。不同风味的正则表达式POSIX 定义的正则表达式 (RE) 有两种形式:扩展正则表达式(ERE) 和基本正则表达式(BRE)。ERE 大致是传统egrep 的那些,而 BRE 大致是传统ed 的那些。这个实现增加了第三种风格:高级正则表达式_wxwidget 正则表达式 非数字字符

Java中普通for循环和增强for循环的对比_for循环10万数据需要时间-程序员宅基地

文章浏览阅读3.4k次,点赞5次,收藏11次。Java中普通for循环和增强for循环的对比_for循环10万数据需要时间

学习PCB设计前的知识扫盲_pcb端子设计基础知识-程序员宅基地

文章浏览阅读2.7k次,点赞13次,收藏97次。0.工厂制作PCB线路板流程1.PCB的结构铜层阻焊丝印本质(PCB画电路板到底在画什么)基础工艺指标2.PCB图中的元素元素布局布线叠层设计3.PCB的设计依据原理图原理图元件库4.PCB的设计流程——总结_pcb端子设计基础知识

Python读取Excel内容;将读取的数据转换为list类型便于切片处理;列表的操作方法;pandas处理DataFrame类型数据;pandas操作;Python几种取整的方法_pandas excel list-程序员宅基地

文章浏览阅读4.5k次,点赞5次,收藏19次。Python读取Excel内容;将读取的数据转换为list类型便于切片处理;列表的操作方法;pandas处理DataFrame类型数据_pandas excel list

nginx日志与监控,日志分析_nginx的日志分析-程序员宅基地

文章浏览阅读4.6k次。在分析服务器运行情况和业务数据时,nginx日志是非常可靠的数据来源,而掌握常用的nginx日志分析命令的应用技巧则有着事半功倍的作用,可以快速进行定位和统计。下面是自己在分析nginx日志时常用命令的一些总结。1.利用grep ,wc命令统计某个请求或字符串出现的次数比如我要统计GET /task/showContent接口在某天的调用次数,则可以使用如下命令: cat _nginx的日志分析

ECharts--中国地图(无敌详细)_echarts中国地图-程序员宅基地

文章浏览阅读5.4w次,点赞64次,收藏262次。使用Echarts绘制中国地图,其中地图点信息由JSON文件编写,前端html直接从JSON文件中读取地区数据,渲染到前端即可。详细介绍用到的各个功能!代码直接复制运行即可!_echarts中国地图

随便推点

三级嵌入式准备(二)_八个gpio引脚最多构成几个按键-程序员宅基地

文章浏览阅读435次,点赞3次,收藏7次。转载来源为https://blog.csdn.net/ReCclay/article/details/79439686 1、嵌入式系统的CPU主要使用的有DSP、ARM以及FPGA。2、DSP适用于数字信号处理的微处理器支持单指令多数据(DIMD)并行处理的指令显著提高音频、视频等数字信号的数据处理效率3、片上系统SOC已成为嵌入式处理器芯片的主流发展趋势它是..._八个gpio引脚最多构成几个按键

OpenStack的容器服务体验-程序员宅基地

文章浏览阅读70次。magnum 是用于 OpenStack 的容器服务。它有以下特点:抽象的容器、节点、服务等集成了用于容器技术的Kubernetes和Docker集成了多租户安全的 Keystone继承了k8s多租户网络安全的 Neutron环境准备在VMware Workstations建台虚拟机,Ubuntu 14.04 LTS,..._openstack 安装好没有容器服务

HDU - 2209 翻纸牌游戏(贪心)_hdu 2209-程序员宅基地

文章浏览阅读420次。 HDU - 2209 翻纸牌游戏 当前的这张牌是否翻转取决于它的前一张牌是否朝上,如果朝上,不翻转,朝下,则翻转,这是贪心的思想,但是,对于第一张牌来说,它的前面没有牌了,所以可以翻转,也可以不翻转,分两种情况来判断,参考的别人的代码 #include&lt;stdio.h&gt;#include&lt;algorithm&gt;#include&lt;string.h&gt;u..._hdu 2209

mysql异常代码c0000005_win7系统因0xc0000005错误导致应用程序无法正常启动的解决方法...-程序员宅基地

文章浏览阅读2k次。很多小伙伴都遇到过win7系统因0xc0000005错误导致应用程序无法正常启动的困惑吧,一些朋友看过网上零散的win7系统因0xc0000005错误导致应用程序无法正常启动的处理方法,并没有完完全全明白win7系统因0xc0000005错误导致应用程序无法正常启动是如何解决的,今天小编准备了简单的解决办法,只需要按照1、右键点击要运行的软件或游戏,在右键菜单中选择“兼容性疑难解答”; 2、让系..._mysql 0xc0000005

UNIX环境高级编程_标准io创建空头文件-程序员宅基地

文章浏览阅读492次。unix环境高级编程笔记_标准io创建空头文件

apt-get update 报错:*** Error in `appstreamcli‘: double free or corruption (fasttop)_sudo apt-get update error in appstreamcli-程序员宅基地

文章浏览阅读1.3k次。环境:ubuntu 16.04在执行apt-get update时直接报错了,错误信息如下:从返回的错误信息可以看出,问题出在“appstreamcli”上。通过以下命令可以解决:sudo apt install appstream/xenial-backportssudo appstreamcli refresh –force亲测可行。..._sudo apt-get update error in appstreamcli