SpringDataJPA入门学习总结笔记

JPA相关的基础概念


ORM思想

  • Object-Relational Mapping 表示对象关系映射,在面向对象的软件开发中,通过ORM,就可以把对象映射到关系型数据库中。
  • 主要目的:
    操作实体类就相当于操作数据库表,不再重点关注sql语句
  • 建立两个映射关系:
    实体类和表的映射关系
    实体类中属性和表中字段的映射关系
  • 实现了ORM思想的框架:
    Mybatis、Hibernate

Hibernate框架

  • 一个开放源代码的对象关系映射框架
  • 对JDBC进行了非常轻量级的对象封装
  • 将POJO与数据库表建立映射关系,是一个全自动的ORM框架

JPA规范

  • JPA的全称是Java Persistence API, 即Java持久化API,是SUN公司推出的一套基于ORM的规范,内部是由一系列的接口和抽象类构成。

  • JPA通过JDK 5.0注解描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

  • 优点:
    ①标准化
    任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。
    ②容器级特性支持
    JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。
    ③简单方便
    在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。
    ④ 查询能力
    JPA的查询语言是面向对象而非面向数据库的,JPA定义了独特的JPQL(Java Persistence Query Language),它是针对实体的一种查询语言,操作对象是实体,能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
    ⑤高级特性
    JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。

  • JPA与hibernate的关系
    JPA规范本质上就是一种ORM规范,注意不是ORM框架——JPA并未提供ORM实现,它只是制订了规范,提供了编程的API接口,具体实现则由服务厂商来提供。

    JPA和Hibernate的关系就像JDBC和JDBC驱动的关系,JPA是规范,Hibernate除了作为ORM框架之外,它也是一种JPA实现。如果使用JPA规范进行数据库操作,底层需要hibernate作为其实现类完成数据持久化工作。
    在这里插入图片描述


JPA入门案例

保存客户

创建maven工程,导入坐标

	<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.hibernate.version>5.0.12.Final</project.hibernate.version>
    </properties>

    <dependencies>
        <!-- junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- hibernate对jpa的支持包 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${project.hibernate.version}</version>
        </dependency>

        <!-- c3p0 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-c3p0</artifactId>
            <version>${project.hibernate.version}</version>
        </dependency>

        <!-- log日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <!-- Mysql  -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.44</version>
        </dependency>

    </dependencies>

创建jpa核心配置文件
要求 必须在resources/META-INF目录下,文件名叫persistence.xml
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <!--
    name 持久化单元名称
    transaction-type 事务管理方式
            JTA:分布式事务管理 RESOURCE_LOCAL:本地事务管理
    -->
    <persistence-unit name="myJPA" transaction-type="RESOURCE_LOCAL">
        <!-- jpa实现方式 -->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

        <properties>
            <!-- 数据库信息 -->
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql:///test"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value=""/>
            <!-- 可选配置:配置jpa实现方式的配置信息-->
            <!-- 显示sql语句 -->
            <property name="hibernate.show_sql" value="true"/>
            <!-- 自动创建数据库表  
                create 程序运行时创建数据库表 如果有先删除表再创建
                update 程序运行时创建表 如果有则不会创建
                none 不会创建表
            -->
            <property name="hibernate.hbm2ddl.auto" value="create"/>
        </properties>

    </persistence-unit>
</persistence>

创建客户的数据库表

/*创建客户表*/
CREATE TABLE customer (
	cust_id BIGINT(32) PRIMARY KEY AUTO_INCREMENT COMMENT '客户编号(主键)',
	cust_name VARCHAR(32) NOT NULL COMMENT '客户名称(公司名称)',
	cust_source VARCHAR(32) DEFAULT NULL COMMENT '客户信息来源',
	cust_industry VARCHAR(32) DEFAULT NULL COMMENT '客户所属行业',
	cust_level VARCHAR(32) DEFAULT NULL COMMENT '客户级别',
	cust_address VARCHAR(128) DEFAULT NULL COMMENT '客户联系地址',
	cust_phone VARCHAR(64) DEFAULT NULL COMMENT '客户联系电话'
);

创建对应数据库表的客户实体类

package com.pojo;

public class Customer {
    private Long id;//主键    
    private String name;//名称
    private String source;//来源
    private String industry;//行业
    private String level;//级别
    private String address;//地址
    private String phone;//联系方式

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public String getIndustry() {
        return industry;
    }

    public void setIndustry(String industry) {
        this.industry = industry;
    }

    public String getLevel() {
        return level;
    }

    public void setLevel(String level) {
        this.level = level;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", source='" + source + '\'' +
                ", industry='" + industry + '\'' +
                ", level='" + level + '\'' +
                ", address='" + address + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}

配置映射关系
实体类和表的关系
在这里插入图片描述
实体类中属性和表字段的映射关系
在这里插入图片描述


创建测试类

public class JpaTest {
  

    //测试jpa的保存
    @Test
    public void save(){
        //创建工厂对象
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJPA");
        //获取实体管理器
        EntityManager manager = factory.createEntityManager();
        //获取事务对象,开启事务
        EntityTransaction transaction = manager.getTransaction();
        transaction.begin();
        //保存操作实现
        Customer customer=new Customer();
        customer.setName("kobe");
        customer.setIndustry("nba");
        manager.persist(customer);
        //提交事务
        transaction.commit();
        //释放资源
        manager.close();
        factory.close();
    }
}

在这里插入图片描述


在数据库中查询结果
在这里插入图片描述


主键的生成策略

自增 要求底层数据库支持自增 mysql

	@GeneratedValue(strategy = GenerationType.IDENTITY) 

序列 要求底层数据库支持序列 oracle

	@GeneratedValue(strategy = GenerationType.SEQUENCE) 

TABLE JPA提供的一种机制,通过一张数据库表完成自增

	@GeneratedValue(strategy = GenerationType.TABLE) 

由程序自动帮我们完成主键自增

	@GeneratedValue(strategy = GenerationType.AUTO) 

JPA操作步骤

  • 1.加载配置文件 创建工厂对象
    静态方法 根据持久化单元名称创建实体管理器工厂
    (创建工厂比较耗时,可以使用静态代码块创建一个公共的工厂)
    Persistence:

  • 2.通过工厂获取实体管理器EntityManager对象
    是一个线程安全的对象,内部维护了数据库信息、缓存信息、所有实体管理器对象
    在这里插入图片描述
    EntityManager对象方法:
    getTransaction 获取事务对象
    persist 保存
    merge 更新
    remove 删除
    find/getReference 根据id查询

  • 3.获取事务对象,开启事务
    在这里插入图片描述
    EntityTransaction对象方法:
    begin 开启事务
    rollback 回滚事务
    commit 提交事务

  • 4.完成操作

  • 5.提交(回滚)事务

  • 6.释放资源


抽取JPA工具类

解决实体管理器工厂浪费资源和耗时问题,通过静态代码块的形式,当程序第一次访问工具类时,创建一个实体管理器工厂对象

public class JpaUtils {
    private static EntityManagerFactory myJPAFactory;

    static {
        myJPAFactory = Persistence.createEntityManagerFactory("myJPA");
    }

    //获取实体管理器
    public static EntityManager getEntityManager(){
        return myJPAFactory.createEntityManager();
    }
}

测试类修改为

//测试jpa的保存
    @Test
    public void save(){
        //获取实体管理器
        EntityManager manager = JpaUtils.getEntityManager();
        //获取事务对象,开启事务
        EntityTransaction transaction = manager.getTransaction();
        transaction.begin();
        //保存操作实现
        Customer customer=new Customer();
        customer.setName("kobe");
        customer.setIndustry("nba");
        manager.persist(customer);
        //提交事务
        transaction.commit();
        //释放资源
        manager.close();
    }

查询客户

//测试jpa的查询
    @Test
    public void findById(){
        //获取实体管理器
        EntityManager manager = JpaUtils.getEntityManager();
        //获取事务对象,开启事务
        EntityTransaction transaction = manager.getTransaction();
        transaction.begin();
        //查询操作实现(将配置中的create改为update)
        Customer customer = manager.find(Customer.class, 1L);
        System.out.println(customer);
        //提交事务
        transaction.commit();
        //释放资源
        manager.close();
    }

在这里插入图片描述


延迟加载与立即加载

使用getReference方法

//测试jpa的查询2
    @Test
    public void findById2(){
        //获取实体管理器
        EntityManager manager = JpaUtils.getEntityManager();
        //获取事务对象,开启事务
        EntityTransaction transaction = manager.getTransaction();
        transaction.begin();
        //查询操作实现(将配置中的create改为update)
        Customer customer = manager.getReference(Customer.class, 1L);
        System.out.println(customer);
        //提交事务
        transaction.commit();
        //释放资源
        manager.close();
    }

结果和之前一模一样
在这里插入图片描述
区别:

  • 使用find方法查询,查询获取的对象就是当前客户对象本身,调用find方法时就会发送sql语句查询数据库
  • 使用getReference方法查询,查询获取的对象是动态代理对象,调用方法时不会立即发送sql语句查询数据库,当调用结果对象时(本例中为输出对象)才会发送sql语句查询数据库
  • 一般使用延迟加载,节省资源

删除客户

//测试jpa的删除
    @Test
    public void remove(){
        //获取实体管理器
        EntityManager manager = JpaUtils.getEntityManager();
        //获取事务对象,开启事务
        EntityTransaction transaction = manager.getTransaction();
        transaction.begin();
        //删除操作实现(将配置中的create改为update)
        Customer customer = manager.getReference(Customer.class, 1L);
        manager.remove(customer);
        //提交事务
        transaction.commit();
        //释放资源
        manager.close();
    }

在这里插入图片描述
运行后查询数据库
在这里插入图片描述


更新客户

数据库初始记录
在这里插入图片描述
更新代码

//测试jpa的更新
    @Test
    public void update(){
        //获取实体管理器
        EntityManager manager = JpaUtils.getEntityManager();
        //获取事务对象,开启事务
        EntityTransaction transaction = manager.getTransaction();
        transaction.begin();
        //删除操作实现(将配置中的create改为update)
        Customer customer = manager.getReference(Customer.class, 1L);
        customer.setAddress("LOS ANGELES");
        customer.setName("KOBE BRYANT");
        manager.merge(customer);
        //提交事务
        transaction.commit();
        //释放资源
        manager.close();
    }

运行,查询结果
在这里插入图片描述
在这里插入图片描述


使用JPQL查询

JPQL全称Java persistence query language,Java持久化查询语言,是面向对象的查询语言,查询的是实体类和类中的属性,语法与SQL类似。

查询全部

//测试jpql查询
    @Test
    public void findAll(){
        //获取实体管理器
        EntityManager entityManager = JpaUtils.getEntityManager();
        //获取事务对象,开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //查询全部
        String jpql="from com.pojo.Customer";
        Query query = entityManager.createQuery(jpql);
        List resultList = query.getResultList();
        System.out.println(resultList);
        //提交事务
        transaction.commit();
        //释放资源
        entityManager.close();
    }

在这里插入图片描述


排序查询

//测试jpql排序查询
    @Test
    public void findOrderById(){
        //获取实体管理器
        EntityManager entityManager = JpaUtils.getEntityManager();
        //获取事务对象,开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //根据id倒序查询全部
        String jpql="from Customer order by id desc";
        Query query = entityManager.createQuery(jpql);
        List<Customer> resultList = query.getResultList();
        for(Customer customer:resultList){
            System.out.println(customer);
        }
        //提交事务
        transaction.commit();
        //释放资源
        entityManager.close();
    }

在这里插入图片描述


统计查询

//测试jpql统计查询
    @Test
    public void findCount(){
        //获取实体管理器
        EntityManager entityManager = JpaUtils.getEntityManager();
        //获取事务对象,开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //统计查询
        String jpql="select count(id) from Customer";
        Query query = entityManager.createQuery(jpql);
        Long count = (Long) query.getSingleResult();
        System.out.println("记录数: "+count);
        //提交事务
        transaction.commit();
        //释放资源
        entityManager.close();
    }

在这里插入图片描述


分页查询

//测试jpql分页查询
    @Test
    public void findPage(){
        //获取实体管理器
        EntityManager entityManager = JpaUtils.getEntityManager();
        //获取事务对象,开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //分页查询
        String jpql="from Customer";
        Query query = entityManager.createQuery(jpql);
        //对分页参数赋值 从1开始,查询1条
        query.setFirstResult(1);
        query.setMaxResults(1);
        List<Customer> resultList = query.getResultList();
        for(Customer customer:resultList){
            System.out.println(customer);
        }
        //提交事务
        transaction.commit();
        //释放资源
        entityManager.close();
    }

在这里插入图片描述


条件查询

//测试jpql条件查询
    @Test
    public void findCondition(){
        //获取实体管理器
        EntityManager entityManager = JpaUtils.getEntityManager();
        //获取事务对象,开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //条件查询 姓名以KO开头的记录
        String jpql="from Customer where name like ?";
        Query query = entityManager.createQuery(jpql);
        query.setParameter(1,"KO%");
        List<Customer> resultList = query.getResultList();
        for(Customer customer:resultList){
            System.out.println(customer);
        }
        //提交事务
        transaction.commit();
        //释放资源
        entityManager.close();
    }

在这里插入图片描述


Spring Data JPA相关的基础概念

Spring Data JPA概述

  • Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。
  • 它提供了包括增删改查等在内的常用功能,且易于扩展,可以极大提高开发效率。
  • Spring Data JPA 让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,在实际的工作工程中,推荐使用Spring Data JPA + ORM(如:hibernate)完成操作,这样在切换不同的ORM框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦

Spring Data JPA的特性

  • SpringData Jpa 极大简化了数据库访问层代码。
  • 使用了SpringDataJpa,我们的dao层中只需要写接口,就自动具有了增删改查、分页查询等方法。

Spring Data JPA 与 JPA和hibernate之间的关系

  • JPA是一套规范,内部是由有接口和抽象类组成的。hibernate是一套成熟的ORM框架,而且Hibernate实现了JPA规范,所以也可以称hibernate为JPA的一种实现方式,我们使用JPA的API编程,意味着站在更高的角度上看待问题(面向接口编程)
  • Spring Data JPA是Spring提供的一套对JPA操作更加高级的封装,是在JPA规范下的专门用来进行数据持久化的解决方案。

SpringDataJPA入门案例

搭建环境

创建工程导入坐标

 <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>5.2.1.RELEASE</spring.version>
        <hibernate.version>5.0.12.Final</hibernate.version>
    </properties>

    <dependencies>
        <!-- spring -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</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-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-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>


        <!-- junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- hibernate对jpa的支持包 -->
        <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-validator</artifactId>
            <version>5.3.5.Final</version>
        </dependency>

        <!-- c3p0 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-c3p0</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

        <!-- log日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        
        <!-- Mysql  -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.44</version>
        </dependency>

        <!-- el beg 使用spring data jpa 必须引入 -->
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>2.2.4</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>javax.el</artifactId>
            <version>2.2.4</version>
        </dependency>
        <!-- el end -->
    </dependencies>

配置spring文件
在resources目录下创建一个spring-data-jpa.xml的springConfig配置文件

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/data/jpa
		http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <!--spring 和 spring-data-jpa的配置 -->
    <!-- 创建entityManager对象交给spring管理 -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置扫描实体类所在的包 -->
        <property name="packagesToScan" value="com.pojo"/>
        <!-- 配置jpa的实现方式 -->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
        </property>
        <!-- jpa的适配器 -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="generateDdl" value="false"/><!-- 是否自动创建数据库表 -->
                <property name="database" value="MYSQL"/><!--数据库类型 -->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/><!-- 支持的特有语法 -->
                <property name="showSql" value="true"/><!-- 是否显示sql语句 -->
            </bean>
        </property>
        <!-- jpa高级特性 -->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
        </property>
    </bean>

    <!-- 创建数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql:///test"/>
        <property name="user" value="root"/>
        <property name="password" value=""/>
    </bean>

    <!-- 整合spring-data-jpa  base-package是dao接口所在的包 -->
    <jpa:repositories base-package="com.dao" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityManagerFactory"/>

    <!-- 事务管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    
    <!-- 声明式事务 -->
    <!-- 配置包扫描 -->
    <context:component-scan base-package="com"/>
</beans>

编写实体类,用jpa注解配置映射关系

跟之前的Customer类一样

@Entity//声明此类是一个实体类
@Table(name = "customer")//配置实体类和表的映射关系 name属性配置数据库表名
public class Customer {
    @Id//声明主键
    @GeneratedValue(strategy = GenerationType.IDENTITY)//配置主键生产策略
    @Column(name = "cust_id")//配置属性和字段的映射关系 name配置数据库字段名
    private Long id;//主键

    @Column(name = "cust_name")
    private String name;//名称

    @Column(name = "cust_source")
    private String source;//来源

    @Column(name = "cust_industry")
    private String industry;//行业

    @Column(name = "cust_level")
    private String level;//级别

    @Column(name = "cust_address")
    private String address;//地址

    @Column(name = "cust_phone")
    private String phone;//联系方式

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public String getIndustry() {
        return industry;
    }

    public void setIndustry(String industry) {
        this.industry = industry;
    }

    public String getLevel() {
        return level;
    }

    public void setLevel(String level) {
        this.level = level;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", source='" + source + '\'' +
                ", industry='" + industry + '\'' +
                ", level='" + level + '\'' +
                ", address='" + address + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}

编写符合SpringDataJpa的dao层接口

dao层规范:

  • 继承接口JpaRepository<T,ID>
  • 提供相应的泛型,T是实体类类型,ID是主键类型

接口代码

public interface CustomerDao extends JpaRepository<Customer,Long> {
//不需要其他代码 
//JpaRepository<T,ID>封装了基本CRUD操作
}


基本CRUD操作

查询操作

直接调用findById方法根据Id查询

@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:spring-data-jpa-config.xml")//指定spring容器的配置信息
public class SpringDataJpaTest {

    @Autowired
    private CustomerDao customerDao;

    @Test
    public void findById(){
        Optional<Customer> customer = customerDao.findById(2L);
        System.out.println(customer);
    }
}

运行结果:
在这里插入图片描述


保存和更新操作

@Test
    public void save(){
        //如果存在的对象id主键不存在,代表保存,存在代表更新
        Customer customer=new Customer();
        customer.setName("AAA");
        customer.setIndustry("FootBall");
        customerDao.save(customer);//保存
        customer.setId(4L);
        customer.setName("BBB");
        customerDao.save(customer);//更新
    }

查询数据库
在这里插入图片描述


删除操作

@Test
    public void delete(){
        customerDao.deleteById(4L);
    }

再次查询数据库
在这里插入图片描述


查询所有操作

@Test
    public void findAll(){
        List<Customer> customers = customerDao.findAll();
        for(Customer customer:customers){
            System.out.println(customer);
        }
    }

结果
在这里插入图片描述


执行过程分析

在自定义的CustomerDao中,并没有提供任何方法就可以使用其中的很多方法。
由于继承了JpaRepository所以我们可以使用这两个接口的所有方法。
但是这些方法都只是一些声明,没有具体的实现方式,那么在 Spring Data JPA中它又是怎么实现的呢?
以debug断点调试的方式,分析Spring Data JPA的的执行过程,以findById方法为例进行分析。
在这里插入图片描述
可以发现注入的customerDao对象,本质上是通过JdkDynamicAopProxy生成的一个代理对象:
在这里插入图片描述
当程序执行的时候,会通过JdkDynamicAopProxyinvoke方法,对customerDao对象生成动态代理对象,这个动态代理对象就是SimpleJpaRepository
调用自定义类的findById方法实际就是调用SimpleJpaRepositoryfindById方法
在这里插入图片描述
Spring Data JPA对JPA操作进行了进一步封装,简化了Dao层代码的开发。


执行过程和原理总结:

  • 通过JdkDynamicAopProxyinvoke方法创建了一个动态代理对象SimpleJpaRepository
  • SimpleJpaRepository中封装了JPA的操作
  • 通过hibernate(封装了JDBC)完成数据库操作

复杂查询

统计总数

 	@Test
    public void count(){
        long count = customerDao.count();
        System.out.println(count);
    }

在这里插入图片描述


判断存在

	@Test
    public void exists(){
        boolean b = customerDao.existsById(3L);
        System.out.println(b);
    }

在这里插入图片描述


getOne() 查询一个

	@Test@Transactional//保证正常运行
    public void getOne(){
        Customer customer = customerDao.getOne(1L);
        System.out.println(customer);
    }

在这里插入图片描述

通过源码getReference方法可以看出getOnefindById的区别是使用了延迟加载
在这里插入图片描述


使用JPQL查询

需要将JPQL通过注解配置到接口方法上

根据名称查询

接口中增加方法

public interface CustomerDao extends JpaRepository<Customer,Long> {

    //根据客户名称查询
    @Query(value = "from Customer where name = ?1")
    Customer findByName(String name);
}

测试类中增加方法

@Test
    public void findByName(){
        Customer customer = customerDao.findByName("JAMES");
        System.out.println(customer);
    }

结果
在这里插入图片描述


根据id和名称查询

接口中增加方法 占位符需要和方法参数的顺序一致, ?后的数字是参数的索引,从1开始

	//根据客户id和名称查询
    @Query(value = "from Customer where id=?1 and name = ?2")
    //等价于@Query(value = "from Customer where name=?2 and id = ?1")
    Customer findByIdAndName(Long id,String name);

测试类中增加方法

	 @Test
    public void findByIdAndName(){
        Customer customer = customerDao.findByIdAndName(2L,"JAMES");
        System.out.println(customer);
    }

结果
在这里插入图片描述


更新操作
springdatajpa的更新/删除操作需要事务支持(添加注解),默认执行结束后回滚,需要添加注解取消回滚

接口方法

//根据id更新名称
    @Query(value = "update Customer set name=?1 where id=?2")
    @Modifying//表示是更新操作
    void updateNameById(String name,Long id);

测试方法

	@Test
	@Transactional//添加事务支持
	@Rollback(false)//取消回滚
    public void updateNameById(){
        customerDao.updateNameById("JAMES23",2L);
    }

运行后查询数据库
在这里插入图片描述


使用SQL查询

需要将SQL通过注解配置到接口方法上
使用@Query中的nativeQuery属性进行标识(true表示MySQL)


查询全部

//查询全部
    @Query(value = "select * from customer",nativeQuery = true)
    List<Object[]> findAllBySql();

测试方法

 @Test
    public void findAllBySql(){
        List<Object[]> lists = customerDao.findAllBySql();
        for(Object[] obj:lists){
            System.out.println(Arrays.toString(obj));
        }
    }

结果:
在这里插入图片描述


模糊查询

接口中添加方法

 //模糊查询
    @Query(value = "select * from customer where cust_name like ?",nativeQuery = true)
    List<Object[]> findByNameWithSql(String name);

测试方法

@Test
public void findByNameWithSql(){
    List<Object[]> lists = customerDao.findByNameWithSql("K%");
    for(Object[] obj:lists){
        System.out.println(Arrays.toString(obj));
    }
}

结果
在这里插入图片描述


方法名规则查询

是对JPQL更深层的封装,只需要按照springdataJPA提供的方法名规则定义方法,不需要配置JPQL语句
方法名的规则:

具体查询
findBy+对象中的属性名称
在这里插入图片描述
例如根据名称查询
在这里插入图片描述
测试方法
在这里插入图片描述
结果
在这里插入图片描述


条件查询
findBy+对象中的属性名称+查询方式
例:模糊查询

接口方法
在这里插入图片描述
测试方法
在这里插入图片描述
结果
在这里插入图片描述


多条件查询
findBy+对象中的属性名称+查询方式+多条件连接符(and|or)
例:根据名称模糊匹配和所属行业精准匹配
接口方法:

Customer findByNameLikeAndIndustry(String name,String industry);

测试方法

@Test
    public void findByNameLikeAndIndustry(){
        Customer customer = customerDao.findByNameLikeAndIndustry("JA%","nba");
        System.out.println(customer);
    }

结果
在这里插入图片描述


Specifications动态查询

方法列表
T findOne(Specification<T> spec) 查询单个对象

List<T> findAll(Specification<T> spec) 查询列表

Page<T> findAll(Specification<T> spec,Pageable pageable) 分页查询全部

List<T> findAll(Specification<T> spec,Sort sort) 排序查询全部

long count(Specification<T> spec) 统计查询

Specification代表查询条件


查询单个

dao接口需要继承JpaSpecificationExecutor<Customer>
在这里插入图片描述
测试方法

//根据客户名称查询
    @Test
    public void findOne(){
        //自定义查询条件 实现Specification接口 实现toPredicate方法 从root获取对象属性 CriteriaBuilder构造查询条件
        Specification<Customer> specification= (Specification<Customer>) (root, criteriaQuery, criteriaBuilder) -> {
            //获取比较的属性
            Path<Object> name = root.get("name");
            //构造查询
            Predicate res = criteriaBuilder.equal(name, "JAMES23");//精确匹配,相当于查询name为JAMES23的记录
            return res;
        };
        Optional<Customer> customer = customerDao.findOne(specification);
        System.out.println(customer);
    }

结果
在这里插入图片描述


多条件查询

//根据客户名称和行业查询
    @Test
    public void findOne2(){
        Specification<Customer> specification= (Specification<Customer>) (root, criteriaQuery, criteriaBuilder) -> {
            //获取比较的属性
            Path<Object> name = root.get("name");
            Path<Object> industry = root.get("industry");
            //构造查询
            Predicate res1 = criteriaBuilder.equal(name, "JAMES23");
            Predicate res2 = criteriaBuilder.equal(industry, "nba");
            Predicate res = criteriaBuilder.and(res1, res2);
            return res;
        };
        Optional<Customer> customer = customerDao.findOne(specification);
        System.out.println(customer);
    }

结果
在这里插入图片描述


模糊查询

//根据客户名称模糊查询
    @Test
    public void findOne3(){
        Specification<Customer> specification= (Specification<Customer>) (root, criteriaQuery, criteriaBuilder) -> {
            //获取比较的属性
            Path<Object> name = root.get("name");
            //构造查询 使用path对象的as方法指定类型
            Predicate res = criteriaBuilder.like(name.as(String.class),"JA%");
            return res;
        };
        Optional<Customer> customer = customerDao.findOne(specification);
        System.out.println(customer);
    }

结果
在这里插入图片描述


排序查询
使用Sort.by()方法进行排序,将返回值作为查询的参数

@Test
    public void findOne3(){
        Specification<Customer> specification= (Specification<Customer>) (root, criteriaQuery, criteriaBuilder) -> {
            //获取比较的属性
            Path<Object> name = root.get("name");
            //构造查询 使用path对象的as方法指定类型
            Predicate res = criteriaBuilder.like(name.as(String.class),"JA%");
            return res;
        };
        //第一个参数排序的顺序 第二个参数排序的属性名
        Sort sort=Sort.by(Sort.Direction.ASC,"id");
        List<Customer> customer = customerDao.findAll(specification,sort);
        System.out.println(customer);
    }

结果
在这里插入图片描述


分页查询

@Test
    public void findOne4(){
        //第一个参数查询的页数 第二个参数显示的个数
        Specification<Customer> specification=null;
        Pageable pageable=PageRequest.of(1,1);
        Page<Customer> page = customerDao.findAll(specification, pageable);
        System.out.println("数据"+page.getContent());
        System.out.println("page"+page.getTotalPages());//总页数
        System.out.println("count"+page.getTotalElements());//总条数
    }

结果
在这里插入图片描述


多表之间的关系和操作多表的步骤

表关系

  • 一对一
  • 一对多
    一的一方为主表,多的一方为从表,需要在从表新建一列作为外键,取值来源于主表的主键
  • 多对多
    需要一张中间表,最少需要两个字段作为外键指向两张表的主键,组成了联合主键

实体类关系

  • 包含 通过实体类的包含关系描述表关系
  • 继承

步骤

  • 明确表关系
  • 确定表关系(描述 通过外键|中间表)
  • 编写实体类,在实体类中描述表关系(包含关系)
  • 配置映射关系

一对多操作

案例:客户(一)和联系人(多)
1 明确表关系:一对多
2 确定表关系
主表:客户 使用之前的客户表
从表:联系人 在从表添加外键

/*创建联系人表*/
CREATE TABLE linkman (
  lkm_id BIGINT(32) PRIMARY KEY AUTO_INCREMENT COMMENT '联系人编号(主键)',
  lkm_name VARCHAR(16)  COMMENT '联系人姓名',
  lkm_gender CHAR(1)  COMMENT '联系人性别',
  lkm_phone VARCHAR(16)  COMMENT '联系人办公电话',
  lkm_mobile VARCHAR(16)  COMMENT '联系人手机',
  lkm_email VARCHAR(64)  COMMENT '联系人邮箱',
  lkm_position VARCHAR(16)  COMMENT '联系人职位',
  lkm_memo VARCHAR(512)  COMMENT '联系人备注',
  lkm_cust_id BIGINT(32) NOT NULL COMMENT '客户id(外键)',
  CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `customer` (`cust_id`)
);

3 编写实体类
客户:在客户实体类中包含一个联系人集合

@Entity//声明此类是一个实体类
@Table(name = "customer")//配置实体类和表的映射关系 name属性配置数据库表名
public class Customer {
    @Id//声明主键
    @GeneratedValue(strategy = GenerationType.IDENTITY)//配置主键生产策略
    @Column(name = "cust_id")//配置属性和字段的映射关系 name配置数据库字段名
    private Long id;//主键

    @Column(name = "cust_name")
    private String name;//名称

    @Column(name = "cust_source")
    private String source;//来源

    @Column(name = "cust_industry")
    private String industry;//行业

    @Column(name = "cust_level")
    private String level;//级别

    @Column(name = "cust_address")
    private String address;//地址

    @Column(name = "cust_phone")
    private String phone;//联系方式
    
    //配置一对多关系 targetEntity 多实体类的字节码
    @OneToMany(targetEntity = Linkman.class)
    //name 从表外键 referencedColumnName 主表主键 
    @JoinColumn(name ="lkm_cust_id" ,referencedColumnName = "cust_id")
    private Set<Linkman> linkmen=new HashSet<>();

    public Set<Linkman> getLinkmen() {
        return linkmen;
    }

    public void setLinkmen(Set<Linkman> linkmen) {
        this.linkmen = linkmen;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public String getIndustry() {
        return industry;
    }

    public void setIndustry(String industry) {
        this.industry = industry;
    }

    public String getLevel() {
        return level;
    }

    public void setLevel(String level) {
        this.level = level;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", source='" + source + '\'' +
                ", industry='" + industry + '\'' +
                ", level='" + level + '\'' +
                ", address='" + address + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}

联系人:在联系人实体类包含一个客户对象

@Entity
@Table(name="linkman")
public class Linkman {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "lkm_id")
    private Long id;

    @Column(name ="lkm_name" )
    private String name;

    @Column(name = "lkm_gender")
    private String gender;

    @Column(name = "lkm_phone")
    private String phone;

    @Column(name = "lkm_mobile")
    private String mobile;

    @Column(name = "lkm_email")
    private String email;

    @Column(name = "km_position")
    private String position;

    @Column(name = "km_memo")
    private String memo;

    //配置表关系
    @ManyToOne(targetEntity = Customer.class)
    //配置外键
    @JoinColumn(name ="lkm_cust_id" ,referencedColumnName = "cust_id")
    private Customer customer;

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPosition() {
        return position;
    }

    public void setPosition(String position) {
        this.position = position;
    }

    public String getMemo() {
        return memo;
    }

    public void setMemo(String memo) {
        this.memo = memo;
    }

    @Override
    public String toString() {
        return "Linkman{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", phone='" + phone + '\'' +
                ", mobile='" + mobile + '\'' +
                ", email='" + email + '\'' +
                ", position='" + position + '\'' +
                ", memo='" + memo + '\'' +
                '}';
    }
}

4 配置映射关系
使用JPA注解配置一对多关系

编写联系人的dao接口

public interface LinkmanDao extends JpaRepository<Linkman,Long>, JpaSpecificationExecutor<Linkman> {
}

在配置文件加入以下配置

	<!-- 自动创建数据库表
               create 程序运行时创建数据库表 如果有先删除表再创建
               update 程序运行时创建表 如果有则不会创建
               none 不会创建表
           -->
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">create</prop>
            </props>
        </property>

编写测试类测试

@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:spring-data-jpa-config.xml")
public class OneToManyTest {
    @Autowired
    private CustomerDao customerDao;

    @Autowired
    private LinkmanDao linkmanDao;

    @Test
    @Transactional
    @Rollback(false)//不自动回滚
    public void save(){
        Customer customer=new Customer();
        customer.setName("kobe");

        Linkman linkman=new Linkman();
        linkman.setName("james");

		customer.getLinkmen().add(linkman);
		
        customerDao.save(customer);
        linkmanDao.save(linkman);
    }
}

运行结果
在这里插入图片描述

查询数据库
客户表
在这里插入图片描述
联系人表
在这里插入图片描述


也可通过联系人维护外键关系

@Test
    @Transactional
    @Rollback(false)//不自动回滚
    public void save(){
        Customer customer=new Customer();
        customer.setName("kobe");

        Linkman linkman=new Linkman();
        linkman.setName("james");

        linkman.setCustomer(customer);

        customerDao.save(customer);
        linkmanDao.save(linkman);
    }

从显示的sql语句中看到少了一条update语句,数据库结果是一样的
在这里插入图片描述


可以在主表一方放弃外键维护提高效率
将注解修改为
在这里插入图片描述


删除从表数据:可以任意删除。
删除主表数据:

  • 有从表数据
    1、在默认情况下,它会把外键字段置为null,然后删除主表数据。如果在数据库的表 结构上,外键字段有非空约束,默认情况就会报错了。
    2、如果配置了放弃维护关联关系的权利,则不能删除(与外键字段是否允许为null, 没有关系)因为在删除时,它根本不会去更新从表的外键字段了。
    3、如果还想删除,使用级联删除引用

  • 没有从表数据引用:随便删


级联操作:指操作一个对象同时操作它的关联对象
使用方法:只需要在操作主体的注解上配置cascade

级联添加
在Customer类的联系人集合属性上的一对多注解配置cascade属性
在这里插入图片描述
测试类

	@Test
    @Transactional
    @Rollback(false)//不自动回滚
    public void add(){
        Customer customer=new Customer();
        customer.setName("kobe");

        Linkman linkman=new Linkman();
        linkman.setName("james");
        Linkman linkman2=new Linkman();
        linkman2.setName("gigi");


        customer.getLinkmen().add(linkman);
        customer.getLinkmen().add(linkman2);
        linkman.setCustomer(customer);
        linkman2.setCustomer(customer);

        //只需要保存客户,即可保存联系人
        customerDao.save(customer);
    }

在这里插入图片描述
查询数据库的联系人表
在这里插入图片描述
级联删除
先修改配置文件自动创建表为update,这样不会在运行时重新创建表清空数据
在这里插入图片描述
测试方法

@Test
    @Transactional
    @Rollback(false)//不自动回滚
    public void delete(){
        customerDao.deleteById(1L);
    }

可以看到先删除了从表
在这里插入图片描述
数据库中记录被删除
在这里插入图片描述


cascade属性
CascadeType.MERGE 级联更新
CascadeType.PERSIST 级联保存:
CascadeType.REFRESH 级联刷新:
CascadeType.REMOVE 级联删除:
CascadeType.ALL 包含所有


多对多操作

案例:用户和角色
用户类

@Entity
@Table(name = "user")
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long id;

    @Column(name = "user_name")
    private String name;

    @ManyToMany(targetEntity = Role.class)
    @JoinTable(name = "user_role",//中间表名称
    joinColumns = {@JoinColumn(name = "user_key",referencedColumnName = "user_id")},//当前对象在中间表的外键
    inverseJoinColumns = {@JoinColumn(name ="role_key" ,referencedColumnName ="role_id" )})//对方对象在中间表的外键
    private Set<Role> roles=new HashSet<>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }
}

角色类

@Entity
@Table(name = "role")
public class Role implements Serializable {

    @Id@GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "role_id")
    private Long id;

    @Column(name = "role_name")
    private String name;

    @ManyToMany(targetEntity = User.class)
    @JoinTable(name = "user_role",//中间表名称
            joinColumns = {@JoinColumn(name = "role_key",referencedColumnName = "role_id")},//当前对象在中间表的外键
            inverseJoinColumns = {@JoinColumn(name ="user_key" ,referencedColumnName ="user_id" )})//对方对象在中间表的外键
    private Set<User> users=new HashSet<>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<User> getUsers() {
        return users;
    }

    public void setUsers(Set<User> users) {
        this.users = users;
    }
}


测试类

@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:spring-data-jpa-config.xml")
public class ManyToMany {

    @Autowired
    private UserDao userDao;
    @Autowired
    private RoleDao roleDao;

    @Test@Transactional@Rollback(false)
    public void add(){
        User user=new User();
        user.setName("kobe");

        Role role=new Role();
        role.setName("basketball-player");

        user.getRoles().add(role);

        userDao.save(user);
        roleDao.save(role);
    }
}

查询结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


在多对多(保存)中,如果双向都设置关系,意味着双方都维护中间表,都会往中间表插入数据,中间表的2个字段又作为联合主键,所以报错,主键重复,解决保存失败的问题:只需要在任意一方放弃对中间表的维护权即可,推荐在被动的一方放弃,配置如下:
修改Role类中的属性

	@ManyToMany(mappedBy = "roles")
    private Set<User> users=new HashSet<>();

级联操作
在User类的多读多注解添加cascade属性
在这里插入图片描述
测试方法

@Test@Transactional@Rollback(false)
    public void addCascade(){
        User user=new User();
        user.setName("kobe");

        Role role=new Role();
        role.setName("basketball-player");

        user.getRoles().add(role);

        userDao.save(user);//只需要保存用户

    }

运行,查看数据库,role表插入记录
在这里插入图片描述
删除记录,先将配置文件中创建数据库方式设置为update

@Test@Transactional@Rollback(false)
    public void delete(){
        userDao.deleteById(1L);
    }

成功删除3个表中数据
在这里插入图片描述


对象导航查询

含义:查询一个对象(get方法查询)的同时,通过此对象可以查询它的关联对象
案例:客户和联系人(一对多)

从客户查询联系人

@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:spring-data-jpa-config.xml")
public class QueryTest {
    @Autowired
    private CustomerDao customerDao;
    
	@Autowired
    private LinkmanDao linkmanDao;
    
    @Test@Transactional
    public void test(){
        Customer customer = customerDao.getOne(1L);
        Set<Linkman> linkmen = customer.getLinkmen();
        for (Linkman linkman : linkmen) {
            System.out.println(linkman);
        }
    }
}

通过客户对象可直接查询联系人
在这里插入图片描述
对象导航查询一到多默认使用延迟加载的形式查询,如果要使用立即加载,在实体类注解配置加上fetch属性。
在这里插入图片描述


从联系人查询客户

@Test@Transactional
    public void test2(){
        Linkman linkman = linkmanDao.getOne(1L);
        Customer customer = linkman.getCustomer();
        System.out.println(customer);
    }

在这里插入图片描述
对象导航查询多到一默认使用立即加载的形式查询。


总结
从一查多 默认使用延迟加载 关联对象是集合 使用立即加载可能浪费资源
从多查一 默认使用立即加载 关联对象是一个对象 所以使用立即加载

发布了66 篇原创文章 · 获赞 302 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_41112238/article/details/104234350