【Java开发实战攻关】「JPA技术专题」带你一同认识和使用JPA框架

news2025/2/22 19:01:16

JPA技术专题

  • 一、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 的关系

  1. JPA 是 Hibernate 的一个抽象(JDBC Interface 与 JDBC驱动的关系)
  2. JPA 本质上就是一种 ORM 规范,不是 ORM 框架,因为 JPA 并未提供 ORM 实现,它只提供了 API 接口,具体的实现由 ORM 厂商提供实现
  3. Hibernate 是 一个 ORM 框架,同时也是一种 JPA 的实现
  4. Hibernate 从3.2版本开始兼容 JPA
  5. Hibernate 使用起来是XML配置文件的方式,而 JPA 是注解的方式,而注解是JDK5.0中自带的,所 以并不需要再引入第三方Jar包,实际上就是来学习如何使用注解的方式来使用 Hibernate

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

  1. ORM 映射元数据:JPA 支持 XML 和 JDK5.0
    注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架将实体对象持久化到数据库表中
  2. JPA 的 API:利用实体对象,操作 JPA 提供的接口进行 CRUD 操作,简化开发者编程代码
  3. 查询语言(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>
  1. 创建 com.javakc.jpa 包

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

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

  4. 在 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
  1. 在 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>
  1. 创建测试类
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 类为实体类,将映射到指定的数据库表

  1. @Table

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

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

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

  1. @Id

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

  1. @GeneratedValue

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

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

  • IDENTITY:采用数据库 ID自增长的方式来自增主键字段,Oracle 不支持这种方式
  • AUTO: JPA自动选择合适的策略,是默认选项
  • SEQUENCE:通过序列产生主键,通过 @SequenceGenerator 注解指定序列名,MySql 不支持这种方式
  • TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植
  1. @Column

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

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

  1. @Transient

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

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

  1. @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());
}
  1. 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);
}
  1. 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);
}
  1. remove:
  • 删除实例。如果实例是被管理的,即与数据库实体记录关联,则同时会删除关联的数据库记录
public void remove() {
    Student student = entityManager.find(Student.class, 1);
    entityManager.remove(student);
}
  1. merge:merge() 用于处理 Entity 的同步。即数据库的插入和更新操作
  • 创建一个新的对象,把临时对象的属性复制到新的对象中,然后对新的对象执行持久化操作。所以新的对象中有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());
}
  • 传入一个游离对象, 即传入的对象有 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());
}
  • 传入一个游离对象, 即传入的对象有 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,以指定是这一关联中的被维护端。同时需要在关系维护端建立外键列指向关系被维护端的主键列
设置关系

// ## 使用 @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;
  1. 测试

  2. 保存

/**
 * 双向 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);

}
  1. 获取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());
}
  1. 获取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<>();
  1. 测试

  2. 保存

/**
 * 单向 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);
}
  1. 获取
/**
 * 默认为关联的 n 端使用懒加载策略
 * 可通过 @OneToMany 的 fetch 属性来修改加载策略关闭懒加载
 */
public void oneToManyFind() {
    ClassRoom classRoom = entityManager.find(ClassRoom.class, 1);
    System.out.println(classRoom.getClassRoomName());
    System.out.println(classRoom.getStudentList().size());
}
  1. 删除
/**
 * 默认删除 1 端的数据前,先把关联的 n 端的外键置空,再删除 1 端数据
 * 可以通过 @OneToMany 的 cascade 属性来修改默认的删除策略
 */
public void oneToManyRemove() {
    ClassRoom classRoom = entityManager.find(ClassRoom.class, 1);
    entityManager.remove(classRoom);
}
  1. 修改
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;
  1. 测试

  2. 保存

/**
 * 先保存 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);

}
  1. 获取
/**
 * 使用左外连接的方式获取 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());
}
  1. 删除1
public void manyToOneRemove1() {
    Student student = entityManager.find(Student.class, 1);
    // ## 删除 n 端
    entityManager.remove(student);
}
  1. 删除2
public void manyToOneRemove2() {
    ClassRoom classRoom = entityManager.find(ClassRoom.class, 1);
    // ## 删除 1 端
    entityManager.remove(classRoom);
}
  1. 修改
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<>();
  1. 测试
  2. 保存
/**
 * 先保存 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<>();
  1. 测试

  2. 保存

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);

}
  1. 获取
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());
}

最后送所有正在努力的大家一句话:

你不一定逆风翻盘,但一定要向阳而生。

期待下次发布好的文章:

山水相逢,我们江湖见。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1123984.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浪涌保护器SPD产品上的8/20波形与10/350波形的含义

浪涌保护器&#xff08;SPD&#xff09;是用于保护电子设备免受电源浪涌或瞬态电压影响的重要装置&#xff0c;我们在选购SPD相关产品时&#xff0c;会发现在其防雷能力放电电流KA后面还有一个重要参数——8/20us或10/350us&#xff0c;这两个参数究竟表示什么意思呢&#xff1…

云服务的划分IaaS,PaaS,SaaS 的区别

云服务只是一个统称&#xff0c;可以分成三大类。 三部分的命名&#xff1a; IaaS&#xff1a;基础设施服务&#xff0c;Infrastructure-as-a-servicePaaS&#xff1a;平台服务&#xff0c;Platform-as-a-serviceSaaS&#xff1a;软件服务&#xff0c;Software-as-a-service

COUNT(*) OVER (PARTITION BY ...)窗口函数——在每一行上执行聚合操作

它用于在查询结果中执行聚合操作&#xff0c;而不会影响查询的分组行数&#xff0c;同时在每个分组内进行计数。 COUNT(): 这表示要计算在窗口内的行数&#xff0c; 代表计算所有行。OVER: 这引入了窗口函数的定义&#xff0c;它告诉数据库引擎在什么样的窗口内执行计数。(PAR…

系列十三、Redis的哨兵机制

一、概述 Sentinel&#xff08;哨兵&#xff09;是Redis的高可用解决方案&#xff0c;由一个或者多个Sentinel实例组成集群&#xff0c;可以监视任意多个主服务器&#xff0c;以及这些服务器下属的所有从服务器&#xff0c;并在被监视的主服务器下线或者宕机时&#xff0c;自动…

python输出小数控制的方法

大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 一、要求较小的精度 将精度高的浮点数转换成精度低的浮点数。 1.round()内置方法 round()不是简单的四舍五入的处理方式。 >>> round(2.5) 2 >>> ro…

AD9371 官方例程HDL详解之JESD204B TX侧时钟生成 (二)

AD9371 系列快速入口 AD9371ZCU102 移植到 ZCU106 &#xff1a; AD9371 官方例程构建及单音信号收发 ad9371_tx_jesd -->util_ad9371_xcvr接口映射&#xff1a; AD9371 官方例程之 tx_jesd 与 xcvr接口映射 AD9371 官方例程 时钟间的关系与生成 &#xff1a; AD9371 官方…

Promise笔记-同步回调-异步回调-JS中的异常error处理-Promis的理解和使用-基本使用-链式调用-七个关键问题

Promise笔记 1. 预备知识1.1 实例对象与函数对象1.2 两种类型的回调函数1. 同步回调2. 异步回调 1.3 JS中的异常error处理1. 错误的类型2. 错误处理&#xff08;捕获与抛出&#xff09;3. 错误对象 2.Promise的理解和使用2.1 Promise是什么1.理解Promise2.Promise 的状态3. Pro…

一种融合偶然和认知不确定性的贝叶斯深度学习RUL框架

_原文&#xff1a; _《《A Bayesian Deep Learning RUL Framework Integrating Epistemic and Aleatoric Uncertainties》 _作者__&#xff1a; _Gaoyang Lia&#xff0c;Li Yangb&#xff0c;Chi-Guhn Leec&#xff0c;Xiaohua Wangd&#xff0c;Mingzhe Ronge _作者单位&am…

selenium元素定位之xpath

一、找父级节点parent xpath&#xff1a;//span[text()保存]/parent::button 说明&#xff1a;先找到span标签&#xff0c;再找到父级button 一、找同级的上方标签preceding-sibling xpath&#xff1a;//span[text()保存]/parent::button/preceding-sibling::button[1] 说明…

找不到mfc140u.dll无法继续执行此代码的5个修复方法分享

是使用计算机的过程中&#xff0c;我们经常会遇到各种各样问题&#xff0c;其中丢失“mfc140u dll”&#xff08;动态链接库&#xff09;是最常见的一种。DLL文件是一种可在多个程序之间共享的代码库&#xff0c;它可以被应用程序在运行时动态加载和卸载。而“mfc140u dll”则是…

Flink实时写入Apache Doris如何保证高吞吐和低延迟

随着实时分析需求的不断增加,数据的时效性对于企业的精细化运营越来越重要。借助海量数据,实时数仓在有效挖掘有价值信息、快速获取数据反馈、帮助企业更快决策、更好的产品迭代等方面发挥着不可替代的作用。 在这种情况下,Apache Doris 作为一个实时 MPP 分析数据库脱颖而出,…

小程序开发——小程序的视图与渲染

1.视图与渲染过程 基本概念&#xff1a; 视图层由WXML页面文件和样式文件WXSS共同组成。事件是视图层和逻辑层沟通的纽带&#xff0c;用户操作触发事件后可通过同名的事件处理函数执行相应的逻辑&#xff0c;处理完成后&#xff0c;更新的数据又将再次渲染到页面上。 WXML页面…

通过流量安全分析发现主机异常

主机异常分析在计算机系统中具有重要意义。以下是主机异常分析的几个关键点&#xff1a; 1、检测安全威胁&#xff1a;主机是计算机系统的核心组件&#xff0c;通过对主机异常进行分析&#xff0c;可以快速检测到潜在的安全威胁&#xff0c;如恶意软件、病毒感染、黑客入侵等。…

python中可变类型与不可变类型详细介绍

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 一.可变类型与不可变类型的特点 1.不可变数据类型 不可变数据类型在第一次声明赋值声明的时候, 会在内存中开辟一块空间, 用来存放这个变量被赋的值, 而这个变量实际上存储的, 并不是被赋予的这个值, 而是存放这个值所在空…

Python获取本机IP地址的三种方式

目录 1、使用专用网址 2、使用自带socket库 3、使用第三方netifaces库 1、使用专用网站 获取的是公网IP。 网址&#xff1a;http://myip.ipip.net 代码&#xff1a; import requestsres requests.get(https://myip.ipip.net, timeout5).textprint(res)具体可以类似这样&#x…

IP-guard客户端WINDOWS的打包方式

IP-guard的打包方式: 第一种:

Bootstrap的旋转器组件

旋转效果可以用来指示状态&#xff0c;比如页面的加载状态。 可以用类spinner-border实现普通旋转的旋转器效果。 用类spinner-grow实现渐渐变大的旋转器效果。 01-最基本的示例代码 <!DOCTYPE html> <html> <head><meta charset"UTF-8">…

Python树莓派开发

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

鞋帽箱包经营小程序商城的作用是什么

线上是很多线下商家破局的方法&#xff0c;企业私域经营很重要。 如今&#xff0c;各行业都在搭建自有私域流量池及自主经营。1000商城模板&#xff0c;海量营销/功能/控件&#xff0c;极简的拖拽拉搭建形式&#xff0c;通过【雨科】平台搭建鞋帽箱包小程序商城&#xff0c;摆…

qwen大模型,推理速度慢,单卡/双卡速度慢,flash-attention安装,解决方案

场景 阿里的通义千问qwen大模型&#xff0c;推理速度慢&#xff0c;单卡/双卡速度慢。 详细&#xff1a; 1、今日在使用qwen-14b的float16版本进行推理&#xff08;BF16/FP16) 1.1 在qwen-14b-int4也会有同样的现象 2、使用3090 24G显卡两张 3、模型加载的device是auto&#x…