其他分享
首页 > 其他分享> > springData-JPA

springData-JPA

作者:互联网

springData-JPA

1.概述:

JPA(Java Persistence API)是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。它的出现主要是为了简化现有的持久化开发工作和整合ORM技术

ORM:通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。本质就是将数据从一种形式转换到另外一种形式。

结束现在Hibernate、TopLink等ORM框架各自为营的局面。JPA是在充分吸收了现有Hibernate、TopLink等ORM框架的基础上发展起来的,具有易于使用,伸缩性强等优点。

注意: JPA不是一种新的ORM框架,它的出现只是用于规范现有的ORM技术,它不能取代现有的Hibernate等ORM框架,相反,采用JPA开发时,我们仍将使用这些ORM框架,只是此时开发出来的应用不在依赖于某个持久化提供商。应用可以在不修改代码的情况下载任何JPA环境下运行,真正做到低耦合,可扩展的程序设计。类似于JDBC,在JDBC出现以前,我们的程序针对特性的数据库API进行编程,但是现在我们只需要针对JDBC API编程,这样能够在不改变代码的情况下就能换成其他的数据库。

JPA是一套规范,不是一套产品。Hibernate是一套产品,如果这些产品实现了JPA规范,那么我们可以叫它们为JPA的实现产品。使用JPA,就可以把我们的应用完全从Hibernate中解脱出来

jpa的优势和特点:

1.标准化
2. 容错级特性的支持
3. 简单方便
4. 查询能力
5. 高级特性

JPA与hibernate的关系

​ JPA规范本质上就是一种ORM规范,注意不是ORM框架——因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,但具体实现则由服务厂商来提供实现。
在这里插入图片描述
在这里插入图片描述图形化理解jpa规范:
在这里插入图片描述

2.jpa入门案例

jpa操作步骤:

1:加载配置文件,创建工厂(实管理体类工厂)对象

EntityManagerFactory factory = Persistence.createEntityManagerFactory(“myDataSource”);

2:通过实体管理器类工厂获取实体管理器

2.1:createEntityManager()内部维护了很多的内容
2.2:维护了数据库的信息
2.3:维护了缓存信息
2.4:维护了所有的实体类管理器对象
2.5:再创建EntityManagerFactory的过程中会根据配置创建数据库表
注意: EntityManagerFactory:创建会非常的消耗资源
EntityManager entityManager = factory.createEntityManager();
特点:是一个线程安全的对象,多个线程访问同一个创建好的对象,不会出现问题。

如何解决 EntityManagerFactory的创建过程浪费资源(耗时的问题?)
解决方案:创建一个公共的EntityManagerFactory对象,使用静态代码块儿(封装为工具类)

3:获取事务对象,开启事务

EntityTransaction transaction = entityManager.getTransaction();

   transaction.begin();  //开始事务
   
   transaction。commit();  //提交事务

4:完成增删改查操作

EntityManager entityManager = factory.createEntityManager();
EntityManager对象:实体类管理器
entityManager.presist():保存
entityManager.merge() ; 更新
entityManager.remove(); 删除
find/getRefrence:根据id查询

5:提交事务或者回滚事务

transaction.commit();

transaction.rollback();

6:释放资源
entityManager.close();
factory.close();

jpa的使用步骤:

目标:搭建jpa环境,使用jpa完成基本的增删改查
1.创建数据库表:

 /*创建客户表*/
    CREATE TABLE cst_customer (
      cust_id bigint(32) NOT NULL 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 '客户联系电话',
      PRIMARY KEY (`cust_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2.创建maven工程,并且导入以下依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>springData-jpa</artifactId>
    <version>1.0-SNAPSHOT</version>

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

<dependencies>

<!-- mysql驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.21</version>
    </dependency>
    <!--  测试依赖  -->

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.7.0</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>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.13.3</version>
    </dependency>

</dependencies>

</project>

3.配置jpa的核心配置文件
3.1:路径:配置到类路径下的一个叫做META-INF的文件下,
3.2:命名:persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!-- 需要配置persistence-unit节点:持久化单元
JTA:分布式管理
RESOURCE_LOCAL:本地事务管理
-->

<persistence-unit name="myDataSource" transaction-type="RESOURCE_LOCAL">
<!--   jpa的实现方式 -->
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 数据库信息 -->
    <properties>
        <property name="javax.persistence.jdbc.user" value="root"/>
        <property name="javax.persistence.jdbc.password" value="2732195202"/>
        <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/component?serverTimezone=GMT%2B8"/>

        <!--  可选配置 配置jpa实现方的配置信息  -->
        <!--  显示sql:输出到控制台  -->
        <property name="hibernate.show_sql" value="true"/>
        <!--  显示sql:自动创建数据库表
        create:程序可运行时创建数据库表,如果有表,先删除表再创建新表
        update:程序运行时创建表,如果表已经存在,不会创建新的表
        none:程序运行时,不会做任何改变
               -->
        <property name="hibernate.hbm2ddl.auto" value="update"/>
    </properties>
</persistence-unit>
</persistence>


4.新建实体类和数据库表做映射关系

/**
 * @author compass
 * @version 1.0
 * @date 2021-03-04 14:27
 */


// 声明该类为实体类
@Entity
//  设置实体类和数据库表的映射关系
@Table(name = "cst_customer")
public class Customer {

    @Id  // 声明主键的配置 主键自增长
    @GeneratedValue(strategy = GenerationType.IDENTITY)  //  主键id :实体类属性和字段映射 声明主键的配置
    @Column(name = "cust_id") //数据库字段名和实体类属性名称的一个映射关系
    private Long custId; //客户的主键

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

    @Column(name="cust_source")
    private String custSource;//客户来源

    @Column(name="cust_level")
    private String custLevel;//客户级别

    @Column(name="cust_industry")
    private String custIndustry;//客户所属行业

    @Column(name="cust_phone")
    private String custPhone;//客户的联系方式

    @Column(name="cust_address")
    private String custAddress;//客户地址


public Long getCustId() {
    return custId;
}

    public void setCustId(Long custId) {
        this.custId = custId;
    }

    public String getCustName() {
        return custName;
    }

    public void setCustName(String custName) {
        this.custName = custName;
    }

    public String getCustSource() {
        return custSource;
    }

    public void setCustSource(String custSource) {
        this.custSource = custSource;
    }

    public String getCustLevel() {
        return custLevel;
    }

    public void setCustLevel(String custLevel) {
        this.custLevel = custLevel;
    }

    public String getCustIndustry() {
        return custIndustry;
    }

    public void setCustIndustry(String custIndustry) {
        this.custIndustry = custIndustry;
    }

    public String getCustPhone() {
        return custPhone;
    }

    public void setCustPhone(String custPhone) {
        this.custPhone = custPhone;
    }

    public String getCustAddress() {
        return custAddress;
    }

    public void setCustAddress(String custAddress) {
        this.custAddress = custAddress;
    }

    public Customer() {
    }

    public Customer(Long custId, String custName, String custSource, String custLevel, String custIndustry, String custPhone, String custAddress) {
        this.custId = custId;
        this.custName = custName;
        this.custSource = custSource;
        this.custLevel = custLevel;
        this.custIndustry = custIndustry;
        this.custPhone = custPhone;
        this.custAddress = custAddress;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "custId=" + custId +
                ", custName='" + custName + '\'' +
                ", custSource='" + custSource + '\'' +
                ", custLevel='" + custLevel + '\'' +
                ", custIndustry='" + custIndustry + '\'' +
                ", custPhone='" + custPhone + '\'' +
                ", custAddress='" + custAddress + '\'' +
                '}';
    }
}


5.测试:想客户表中插入一条数据

/**
 * @author compass
 * @version 1.0
 * @date 2021-03-04 14:52
 */

/**
 * jpa的使用步骤:
 *          1:加载配置文件,创建工厂(实管理体类工厂)对象
 *          2:通过实体管理器类工厂获取实体管理器
 *          3:获取事务对象,开启事务
 *          4:完成增删改查操作
 *          5:提交事务或者回滚事务
 *          6:释放资源
 */
public class JpaTest {
   @Test
    void insert(){

        //1:加载配置文件,创建工厂(实管理体类工厂)对象
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("myDataSource");

        //2:通过实体管理器类工厂获取实体管理器
        EntityManager entityManager = factory.createEntityManager();

        //3:获取事务对象,开启事务
        EntityTransaction transaction = entityManager.getTransaction();
       transaction.begin();

       //添加一个客户到数据库表中
       Customer customerObject = new Customer();
       customerObject.setCustId(null);
       customerObject.setCustName("compass");
       customerObject.setCustSource("随机");
       customerObject.setCustLevel("小白");
       customerObject.setCustIndustry("java开发");
       customerObject.setCustAddress("未知");
       customerObject.setCustPhone("123456");

       // 4:保存一条客户信息到数据库表
       entityManager.persist(customerObject);

       //5:提交事务或者回滚事务
       transaction.commit();

       // 6:释放资源
        entityManager.close();
        factory.close();



   }
}

3.主键生成策略

@GeneratedValue(strategy = GenerationType.IDENTITY) // 主键id :实体类属性和字段映射 声明主键的配置

4.工具类的提取

解决 EntityManagerFactory的创建过程浪费资源(耗时的问题)。

/**
 * @author compass
 * @version 1.0
 * @date 2021-03-04 17:10
 */
public class JpaUtils {
    private static EntityManagerFactory factory;

    static  {
        //1.加载配置文件,创建entityManagerFactory
        factory = Persistence.createEntityManagerFactory("myDataSource");
    }

    /**
     * 获取EntityManager对象
     */
    public static EntityManager getEntityManager() {
        return factory.createEntityManager();
    }

}

延迟加载和立即加载(find和getReference的区别)

Customer customer = entityManager.find(Customer.class, 1l);

    
        方式1: find(需要查询的实体类字节码, 需要查询的id);
        1.查询的对象就是当前客户对象本身
        2.在调用find方法的时候,就会发送sql语句到数据库进行查询
       

优先使用方式2:因为这样可以大大的提高性能

Customer customer = entityManager.getReference(Customer.class, 1l);

        方式2: getRefresh(需要查询的实体类字节码, 需要查询的id);
        1.使用getRefresh方法是一个动态代理对象
        2.调用getRefresh方法不会立即发送sql语句进行查询
        当调用查询对象结果集的时候才会发送sql语句到数据库进行查询
        延迟加载:得到的时候一个动态代理对象,什么时候用,什么时候发送sql进行查询

5.jpa的增删改查操作

/**
 * @author compass
 * @version 1.0
 * @date 2021-03-04 14:52
 */

/**
 * jpa的使用步骤:
 *          1:加载配置文件,创建工厂(实管理体类工厂)对象
 *          2:通过实体管理器类工厂获取实体管理器
 *          3:获取事务对象,开启事务
 *          4:完成增删改查操作
 *          5:提交事务或者回滚事务
 *          6:释放资源
 */
public class JpaTest {
   @Test
    void insert1(){

       //1:加载配置文件,创建工厂(实管理体类工厂)对象
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("myDataSource");

        //2:通过实体管理器类工厂获取实体管理器
        EntityManager entityManager = factory.createEntityManager();

        //3:获取事务对象,开启事务
        EntityTransaction transaction = entityManager.getTransaction();
       transaction.begin();

       //添加一个客户到数据库表中
       Customer customerObject = new Customer();
       customerObject.setCustId(null);
       customerObject.setCustName("compass");
       customerObject.setCustSource("随机");
       customerObject.setCustLevel("小白");
       customerObject.setCustIndustry("java开发");
       customerObject.setCustAddress("未知");
       customerObject.setCustPhone("123456");

       // 4:保存一条客户信息到数据库表
       entityManager.persist(customerObject);

       //5:提交事务或者回滚事务
       transaction.commit();


       // 6:释放资源
        entityManager.close();
        factory.close();

   }

   @Test
   void insert2(){
       //通过工具类获取entityManager对象
       EntityManager entityManager = JpaUtils.getEntityManager();

       //获取事务对象
       EntityTransaction transaction = entityManager.getTransaction();
       transaction.begin();

       //向数据库表中插入条数据
       Customer customerObject = new Customer();
       customerObject.setCustId(null);
       customerObject.setCustName("compass");
       customerObject.setCustSource("随机");
       customerObject.setCustLevel("小白");
       customerObject.setCustIndustry("java开发");
       customerObject.setCustAddress("未知");
       customerObject.setCustPhone("123456");

       entityManager.persist(customerObject);
       transaction.commit();

   }

   @Test
    void  delete(){


       //通过工具类 获取entityManager 对象
       EntityManager entityManager = JpaUtils.getEntityManager();
       //获取事务对象
       EntityTransaction transaction = entityManager.getTransaction();

       //开启事务
       transaction.begin();

       //先查询需要删除的对象
       Customer customer = entityManager.find(Customer.class, 2l);
       //把查询出来的对象传递进去 进行删除
       entityManager.remove(customer);

       // 提交事务
       transaction.commit();

   }

   @Test
    void update(){

       // 通过工具类获取 entityManager 对象
       EntityManager entityManager = JpaUtils.getEntityManager();

       //获取事务对象
       EntityTransaction transaction = entityManager.getTransaction();

       //开启事务
       transaction.begin();

       //先将需要修改的对象查询出来
       Customer customer = entityManager.find(Customer.class, 1l);

       //修改customer对象的属性
       customer.setCustName("卡夫卡");

       //传入修改后的对象进行修改数据库表中的记录
       Customer queryOutCustomer = entityManager.merge(customer);

       //提交事务
       transaction.commit();
   }

   @Test
    void query(){

       EntityManager entityManager = JpaUtils.getEntityManager();

       EntityTransaction transaction = entityManager.getTransaction();

       transaction.begin();

       /**
        * 方式1: find(需要查询的实体类字节码, 需要查询的id);
        * 1.查询的对象就是当前客户对象本身
        * 2.在调用find方法的时候,就会发送sql语句到数据库进行查询
        */
//       Customer customer = entityManager.find(Customer.class, 1l);

       /**
        * 方式2: find(需要查询的实体类字节码, 需要查询的id);
        * 1.使用getRefresh方法是一个动态代理对象
        * 2.调用getRefresh方法不会立即发送sql语句进行查询
        * 当调用查询对象结果集的时候才会发送sql语句到数据库进行查询
        * 延迟加载:得到的时候一个动态代理对象,什么时候用,什么时候发送sql进行查询
        */
       Customer customer = entityManager.getReference(Customer.class, 1l);

       System.out.println(customer);

       transaction.commit();

   }

}

6.jpql操作

接下需要用到的数据库表:



SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for cst_customer
-- ----------------------------
DROP TABLE IF EXISTS `cst_customer`;
CREATE TABLE `cst_customer`  (
  `cust_id` bigint NOT NULL AUTO_INCREMENT,
  `cust_address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `cust_industry` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `cust_level` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `cust_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `cust_phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `cust_source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`cust_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of cst_customer
-- ----------------------------
INSERT INTO `cst_customer` VALUES (1, '未知', 'java开发', '小白', '卡夫卡', '123456', '不详');
INSERT INTO `cst_customer` VALUES (2, '未知', 'java开发', '小白', 'root', '123456', '不详');
INSERT INTO `cst_customer` VALUES (3, '未知', 'linux开发', '小白', 'user', '123456', '随机');
INSERT INTO `cst_customer` VALUES (4, '未知', 'linux开发', '小白', 'test', '123456', '随机');
INSERT INTO `cst_customer` VALUES (5, '未知', 'html开发', '小白', 'jack', '123456', '随机');
INSERT INTO `cst_customer` VALUES (6, '未知', 'html开发', '小白', 'tom', '123456', '随机');
INSERT INTO `cst_customer` VALUES (7, '未知', 'JavaScript开发', '小白', 'admin', '123456', '随机');
INSERT INTO `cst_customer` VALUES (8, '未知', 'JavaScript开发', '小白', 'compass', '123456', '随机');
INSERT INTO `cst_customer` VALUES (10, '未知', 'html开发', '小白', 'exclusive', '123456', '随机');

SET FOREIGN_KEY_CHECKS = 1;


/**
 * @author compass
 * @version 1.0
 * @date 2021-03-04 20:33
 */
public class JpqlTest {

    /**
     * jpql :查询全部
     *      jpql:from Customer
     *      sql:select * from cst_customer
     */

    @Test
    void queryAll(){
        //1.获取 entityManager 对象
        EntityManager entityManager = JpaUtils.getEntityManager();

        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();

        //3.查询全部
        String sql="from com.compass.hibernate.Entity.Customer";
        //创Query对象 :执行jpql的对象
        Query query = entityManager.createQuery(sql);

        //封装查询结果集
        List customerList = query.getResultList();
        for (Object customer : customerList) {
            System.out.println(customer);
        }

        //4.提交事务
        transaction.commit();
        //5.释放资源
        entityManager.close();

    }

    @Test
    void queryOrderBy(){

        //1.获取 entityManager 对象
        EntityManager entityManager = JpaUtils.getEntityManager();

        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();

        //3.查询全部 并且按升序排列 asc:升序  desc:降序
        String sql="from com.compass.hibernate.Entity.Customer order by custId asc";
        //创Query对象 :执行jpql的对象
        Query query = entityManager.createQuery(sql);

        //封装查询结果集
        List customerList = query.getResultList();
        for (Object customer : customerList) {
            System.out.println(customer);
        }

        //4.提交事务
        transaction.commit();
        //5.释放资源
        entityManager.close();
    }

    @Test
    void queryCount(){

        //1.获取 entityManager 对象
        EntityManager entityManager = JpaUtils.getEntityManager();

        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();

        //3.统计表中的记录总数
        String sql="select count(custId) from Customer";
        //创Query对象 :执行jpql的对象
        Query query = entityManager.createQuery(sql);

        //封装查询结果集
        List customerList = query.getResultList();
        for (Object customer : customerList) {
            System.out.println(customer);
        }

        //4.提交事务
        transaction.commit();
        //5.释放资源
        entityManager.close();
    }

    @Test
    void queryPaging(){

        //1.获取 entityManager 对象
        EntityManager entityManager = JpaUtils.getEntityManager();

        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();

        //3.查询全部 并且设置分页条件
        String sql="from Customer where custId>2";
        //创Query对象 :执行jpql的对象
        Query query = entityManager.createQuery(sql);

        //设置分页查询的起始索引
        query.setFirstResult(0);

        //设置分页查询每页显示的条数
        query.setMaxResults(4);

        //封装查询结果集
        List customerList = query.getResultList();
        for (Object customer : customerList) {
            System.out.println(customer);
        }

        //4.提交事务
        transaction.commit();
        //5.释放资源
        entityManager.close();
    }

    @Test
    void queryGroupBy(){

        //1.获取 entityManager 对象
        EntityManager entityManager = JpaUtils.getEntityManager();

        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();

        //3.查询全部 并且按 custIndustry 进行分组
        String sql="from Customer  group by custIndustry ";
        //创Query对象 :执行jpql的对象
        Query query = entityManager.createQuery(sql);


        //封装查询结果集
        List customerList = query.getResultList();
        for (Object customer : customerList) {
            System.out.println(customer);
        }

        //4.提交事务
        transaction.commit();
        //5.释放资源
        entityManager.close();
    }

    @Test
    void queryLike(){

        //1.获取 entityManager 对象
        EntityManager entityManager = JpaUtils.getEntityManager();

        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();

        //3.查询全部模糊查询
        String sql="from Customer where custIndustry like ?1";
        //创Query对象 :执行jpql的对象
        Query query = entityManager.createQuery(sql);

        //设置模糊查询占位符
      query.setParameter(1,"java%");


        //封装查询结果集
        List customerList = query.getResultList();
        for (Object customer : customerList) {
            System.out.println(customer);
        }

        //4.提交事务
        transaction.commit();
        //5.释放资源
        entityManager.close();
    }


}

7.springDataJpa

1.概述:

Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!

​ Spring Data JPA 让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,在实际的工作工程中,推荐使用Spring Data JPA + ORM(如:hibernate)完成操作,这样在切换不同的ORM框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦。

​ JPA是一套规范,内部是有接口和抽象类组成的。hibernate是一套成熟的ORM框架,而且Hibernate实现了JPA规范,所以也可以称hibernate为JPA的一种实现方式,我们使用JPA的API编程,意味着站在更高的角度上看待问题(面向接口编程)。

在这里插入图片描述

​ Spring Data JPA是Spring提供的一套对JPA操作更加高级的封装,是在JPA规范下的专门用来进行数据持久化的解决方案。

2.springDataJpa快速入门

环境搭建:
使用Spring Data JPA,需要整合Spring与Spring Data JPA,并且需要提供JPA的服务提供者hibernate,所以需要导入spring相关坐标,hibernate坐标,数据库驱动坐标等。


<properties>
        <spring.version>4.2.4.RELEASE</spring.version>
        <hibernate.version>5.0.7.Final</hibernate.version>
        <slf4j.version>1.7.30</slf4j.version>
        <log4j.version>2.12.1</log4j.version>
        <c3p0.version>0.9.1.2</c3p0.version>
        <mysql.version>8.0.21</mysql.version>
    </properties>

    <dependencies>
        <!-- junit单元测试 -->

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>

        <!-- spring beg -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.8</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-context-support</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>

        <!-- spring end -->

        <!-- hibernate beg -->
        <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.2.1.Final</version>
        </dependency>
        <!-- hibernate end -->

        <!-- c3p0 beg -->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>${c3p0.version}</version>
        </dependency>
        <!-- c3p0 end -->

        <!-- log end -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <!-- log end -->


        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.9.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</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 -->

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>

2.1:在Resources目录下新建applicationContext.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: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 的配置-->

    <!--1.创建entityManagerFactory对象交给spring容器管理-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- packagesToScan :实体类所在包  -->
        <property name="packagesToScan" value="com.compass.hibernate.Entity"/>
        <!--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" />
                <!--是否显示sql -->
                <property name="showSql" value="true" />
            </bean>
        </property>
    </bean>

    <!--2. 创建数据库连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="root"/>
        <property name="password" value="root"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/component?serverTimezone=GMT%2B8"/>
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
    </bean>

    <!--3.整合spring dataJpa  base-package:dao包所在位置 -->
    <jpa:repositories base-package="com.compass.hibernate.dao" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityManagerFactory" />

    <!--4.配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <!-- 4.txAdvice-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!-- 5.aop-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.compass.hibernate.service.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
    </aop:config>


    <!--5.声明式事务 -->

    <!-- 6. 配置包扫描,使用注解开发 -->
    <context:component-scan base-package="com.compass.hibernate" />

</beans>

2.2编写一个符合springDataJpa的dao接口

只写接口,不需要写实现类
dao接口需要继承两个接口(JpaSpeciffcationExecutor和JpaRepository)
需要提供接口的泛型,也就实体类的class

源码可知:JpaRepository这个接口中实现了基本的增删改查方法:

在这里插入图片描述

/**
 * 只要实现这两个接口:就具备了 基本的增删改查
 * JpaRepository<实体类Class,主键Id类型>
 * JpaSpecificationExecutor<实体类Class>
 */
public interface CustomerDao  extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {



}

2.3:编写实体类和数据库表实现映射关系

/**
 * @author compass
 * @version 1.0
 * @date 2021-03-04 14:27
 */


// 声明该类为实体类
@Entity
//  设置实体类和数据库表的映射关系
@Table(name = "cst_customer")
public class Customer {


    /**
     *  @GeneratedValue(strategy = GenerationType.IDENTITY)  //  主键id :实体类属性和字段映射 声明主键的配置
     *   GenerationType.IDENTITY:主键自增长:底层数据库必须支持自增长
     *   GenerationType.SEQUENCE:底层数据库必须支持序列(oracle)
     *   GenerationType.TABLE: jpa提供的一种机制,通过一张数据库表的形式完成自增,底层数据库不需要支持自增或序列
     *   GenerationType.AUTO:由程序自动选择主键生成策略
     */

    @Id  // 声明主键的配置 主键自增长
    @GeneratedValue(strategy = GenerationType.IDENTITY)  //  主键id :实体类属性和字段映射 声明主键的配置
    @Column(name = "cust_id") //数据库字段名和实体类属性名称的一个映射关系
    private Long custId; //客户的主键

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

    @Column(name="cust_source")
    private String custSource;//客户来源

    @Column(name="cust_level")
    private String custLevel;//客户级别

    @Column(name="cust_industry")
    private String custIndustry;//客户所属行业

    @Column(name="cust_phone")
    private String custPhone;//客户的联系方式

    @Column(name="cust_address")
    private String custAddress;//客户地址


    public Long getCustId() {
    return custId;
    }

    public void setCustId(Long custId) {
        this.custId = custId;
    }

    public String getCustName() {
        return custName;
    }

    public void setCustName(String custName) {
        this.custName = custName;
    }

    public String getCustSource() {
        return custSource;
    }

    public void setCustSource(String custSource) {
        this.custSource = custSource;
    }

    public String getCustLevel() {
        return custLevel;
    }

    public void setCustLevel(String custLevel) {
        this.custLevel = custLevel;
    }

    public String getCustIndustry() {
        return custIndustry;
    }

    public void setCustIndustry(String custIndustry) {
        this.custIndustry = custIndustry;
    }

    public String getCustPhone() {
        return custPhone;
    }

    public void setCustPhone(String custPhone) {
        this.custPhone = custPhone;
    }

    public String getCustAddress() {
        return custAddress;
    }

    public void setCustAddress(String custAddress) {
        this.custAddress = custAddress;
    }

    public Customer() {
    }

    public Customer(Long custId, String custName, String custSource, String custLevel, String custIndustry, String custPhone, String custAddress) {
        this.custId = custId;
        this.custName = custName;
        this.custSource = custSource;
        this.custLevel = custLevel;
        this.custIndustry = custIndustry;
        this.custPhone = custPhone;
        this.custAddress = custAddress;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "custId=" + custId +
                ", custName='" + custName + '\'' +
                ", custSource='" + custSource + '\'' +
                ", custLevel='" + custLevel + '\'' +
                ", custIndustry='" + custIndustry + '\'' +
                ", custPhone='" + custPhone + '\'' +
                ", custAddress='" + custAddress + '\'' +
                '}';
    }
}

2.4springDataJpa 增删改查操作:


/**
 * @author compass
 * @version 1.0
 * @date 2021-03-05 21:25
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class SpringDataJpaTest {

   @Autowired
   private CustomerRepository customerRepository;

    @Test
  public   void  queryOne(){

        Customer customer = customerRepository.findOne(1l);
        System.out.println(customer);
    }

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

    @Test
    public void insert(){
        Customer customer = new Customer();
        customer.setCustId(null);
        customer.setCustAddress("未知");
        customer.setCustIndustry("ui设计");
        customer.setCustLevel("大佬");
        customer.setCustPhone("123456");
        customer.setCustSource("随机");
        customer.setCustName("卡夫瑞");

        /**
         * save: 更新或者修改
         * 当传入的对象有主键id属性的时候就为修改,如果没有id属性的时候则为保存
         */
        customerRepository.save(customer);


    }

    @Test
    public   void  update(){

        Customer customer = new Customer();
        customer.setCustId(9l);
        customer.setCustAddress("未知");
        customer.setCustIndustry("运维");
        customer.setCustLevel("大佬");
        customer.setCustPhone("123456");
        customer.setCustSource("随机");
        customer.setCustName("卡夫瑞");

        /**
         * save: 更新或者修改
         * 当传入的对象有主键id属性的时候就为修改,如果没有id属性的时候则为保存
         */
        Customer selectLaterCustomer = customerRepository.save(customer);

    }

    @Test
    public  void delete(){

        customerRepository.delete(9l);
    }

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

    @Test
    public  void  exists(){
        // 判断id 为 1 的记录是否存在
        boolean flag = customerRepository.exists(1l);
        System.out.println(flag);

    }

    /**getOne和findOne的区别
     * getOne: 立即加载,立即发送sql到数据库
     * findOne:懒加载:等需要调用结果集的时候才会发送sql到数据库查询数据
     */


}

8.springDataJpa执行原理:

在这里插入图片描述
1.通过JdkDynamicAopProxy的invoke方法创建了一个动态代理对象(SimpleJpaRepository)
2. SimpleJpaRepository当中封装了JPA的操作(借助JPA的api完成数据库的CURD)
3. 通过hibernate完成数据库操作(封装了jdbc)

9.Spring Data JPA的查询方式

1.首先需要继承者两个接口:
在这里插入图片描述1.1.使用Query注解自定义查询方法:

public interface CustomerRepository  extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {

    /**
     * 使用jpql Query注解查询:根据客户名称查询
     * jpql: from Customer where customerName=?
     */
    @Query(value="from Customer where custName= ?1 ")
     Customer findJpqlBycustomerName(String customerName);

}

1.2Test自定义的查询方法:

    @Test
    public  void  queryByCustomerName(){

        Customer customer = customerRepository.findJpqlBycustomerName("root");
        System.out.println(customer);
    }
    

2.Query注解sql查询:
2.1.在Dao接口中写上此方法:
nativeQuery : false(使用jpql查询,默认值) | true(使用本地查询 :sql查询)

    /**
     * nativeQuery : false(使用jpql查询,默认值) | true(使用本地查询 :sql查询)
     * sql查询: 使用sql的形式查询
     * @return
     */
    @Query(value="select * from cst_customer" ,nativeQuery=true)
    List<Customer> queryAllSql();

2.测试…

3.Query注解sql查询(带参数):

    /**
     * 占位符:?1:表示从参数列表中取第一个参数作为占位符的值  ?1 == industry
     * 查询 java开发
     * @return
     */
    @Query(value="select * from cst_customer where cust_industry=?1",nativeQuery=true)
    List<Customer> queryAllByIndustrySql(String industry);

4.方法命名基本查询:

根据方法名称来查询:对jpql规范更加深层次的封装(定义在Dao接口中)

1.我们只需要根据SpringDataJpa提供的方法名称定义方法,不需要在配置jpql语句,就可以进行查询
2findBy:代表查询 +对象中的属性名称(首字母大写,也就是查询条件)+ 查询方式(isNull ,Like等)

3.在springDataJpa 会根据名称进行分析 findBy from 实体类, 属性名称 where custName=?
4.如果是多个条件使用And进行拼接 :findByCustNameAndCustIndustry

  /**
     * 根据方法名称来查询:对jpql规范更加深层次的封装
     * 1.我们只需要根据SpringDataJpa提供的方法名称定义方法,不需要在配置jpql语句,就可以进行查询
     * 2. findBy:代表查询 +对象中的属性名称(首字母大写,也就是查询条件)
     * 3.在springDataJpa 会根据名称进行分析 findBy from 实体类, 属性名称 where custName=?
     *
     */
    List<Customer> findByCustName(String custName);

5.方法命名条件查询

dao接口代码:

    /**
     * 根据 industry 进行模糊查询
     * 1.我们只需要根据SpringDataJpa提供的方法名称定义方法,不需要在配置jpql语句,就可以进行查询
     * 2. findBy:代表查询 +对象中的属性名称(首字母大写,也就是查询条件)+ 查询方式(isNull ,Like等)
     * 3.在springDataJpa 会根据名称进行分析 findBy from 实体类, 属性名称 where custName=?
     * 4.如果是多个条件使用And进行拼接  :findByCustNameAndCustIndustry
     */
    List<Customer> findByCustIndustryLike(String industry);

测试代码:


  @Test
    public  void findByIndustryLike(){
        List<Customer> industryList= customerRepository.findByCustIndustryLike("j%");
        for (Customer customer : industryList) {
            System.out.println(customer);
        }
    }
    

6.多条件查询

1、我们只需要根据SpringDataJpa提供的方法名称定义方法,不需要在配置jpql语句,就可以进行查询

2、 findBy:代表查询 +对象中的属性名称(首字母大写,也就是查询条件)+ 查询方式(isNull ,Like等)+多条件连接符( or | and)

3、在springDataJpa 会根据名称进行分析 findBy from 实体类, 属性名称 where custName=?

4、如果是多个条件使用And进行拼接 :findByCustNameAndCustIndustry

     //根据 industry 进行模糊查询
    List<Customer> findByCustIndustryAndCustSource(String industry , String source);

10.Specification动态查询

Specification动态查询介绍:

有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。

Specification:方法列表:

1、T findOne(Specification spec); //查询单个对象

2、List findAll(Specification spec); //查询列表

3、查询全部,分页
pageable:分页参数
返回值:分页pageBean(page:是springdatajpa提供的)
Page findAll(Specification spec, Pageable pageable);

4、查询列表
Sort:排序参数
List findAll(Specification spec, Sort sort);

5、ong count(Specification spec);//统计查询

Specification:查询条件
自定义我们自己的Specification的实现类
predicate toPerdicate(root root,CriteriaQuery<?> query,CriterBuilder builder);
封装查询条件:
root:查询的根对象,查询的任何属性都可以从跟对象中获取
CriteriaQuery:顶层查询对象,自定义查询方式
CriterBuilde:查询构造器(内置很多查询条件)

1、查询单个对象

    // 查询单个对象
    @Test
    public  void queryByCustomerName(){

        /**
         * Specification:实现Specification接口<需要查询的对象类型>
         * Root:获取需要查询的对象属性
         * CriteriaQuery:顶层查询对象,自定义查询方式
         * CriteriaBuilder:构造查询条件,内部封装了很多查询条件
         */

        Specification specification =  new Specification<Customer>() {
            @Override // 采用匿名内部类,构建查询条件
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //1.获取需要比较的属性名称(注意不是字段名)
                Path custName = root.get("custName");
                //2.构造查询条件
                Predicate predicate = criteriaBuilder.equal(custName, "root");//进行精准匹配(比较的属性,比较属性的取值)
                return predicate;
            }
        };
        //根据客户名称查询
        Customer customer = customerRepository.findOne(specification);
        System.out.println(customer);
    }

2.多条件查询

//多条件查询
    @Test
    public void moreConditionSelect(){

      Specification specification =  new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Path custName = root.get("custName");
                Path source = root.get("custSource");
                Predicate predicate1 = criteriaBuilder.equal(custName, "root");
                Predicate predicate2 = criteriaBuilder.equal(source, "不详");

                //将多个查询条件组合到一起(or | and )
                Predicate predicate = criteriaBuilder.and(predicate1, predicate2);

                return predicate;
            }
        };
       Customer customer = customerRepository.findOne(specification);

            System.out.println(customer);
    }

3.模糊查询


    /**
     * 模糊查询 根据产业 industry字段进行模糊查询
     * gt ,lt,ge,le,like 得到path对象,根据path对象知道比较的参数类型 再去进行比较
     * 指定参数类型path.as(参数类型的字节码对象)
     */
    @Test
    public void selectLike(){

     Specification specification =  new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Path industry = root.get("custIndustry");
                Predicate predicate = criteriaBuilder.like(industry.as(String.class), "java%");
                return predicate;
            }
        };

        List<Customer> customerList = customerRepository.findAll(specification);
        for (Customer customer : customerList) {
            System.out.println(customer);
        }

    }

4.模糊查询 根据产业 industry字段进行模糊查询 + 排序 +分页

    /**
     * 模糊查询 根据产业 industry字段进行模糊查询 + 排序 +分页
     * gt ,lt,ge,le,like 得到path对象,根据path对象知道比较的参数类型 再去进行比较
     * 指定参数类型path.as(参数类型的字节码对象)
     * Sort:排序对象 参数1:排序方式(asc,desc) 参数2:按照那个字段进行排序
     * 在创建 PageRequest 对象的时候 需要通过构造方法传递两个参数 参数1: 查询索引位置(从0开始),参数2:每页的数据大小
     * getTotalElements: 获取查询出来的数据中有多少条数据
     * getContent:获取查询出来的数据结果集
     * totalPages:获取总页数
     */
    @Test
    public void Page() {

        Specification specification = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {

                Path industry = root.get("custIndustry");
                Predicate predicate = criteriaBuilder.like(industry.as(String.class), "java%");
                return predicate;
            }
        };
        Sort sort = new Sort(Sort.Direction.ASC,"custId");
        //在创建 PageRequest 对象的时候 需要通过构造方法传递两个参数 参数1: 查询索引位置(从0开始),参数2:每页的数据大小
        Pageable pageable = new PageRequest(0,2,sort);
        Page<Customer>  page = customerRepository.findAll(specification,pageable);
        for (Customer customer : page) {
            System.out.println(customer);
        }


    }
}

11.多表之间的关系映射和操作

11.1表与表之间的关系
表关系

一对一(单张表,一个主键)

一对多:(两张表,两个主键,一个外键)

一的一方:主表(一个主键)

多的一方:从表(一个主键,新建一个字段组作为外键,取值来源于主表的主键)

多对多(三张表,两个主键,两个外键)

中间表:中间表两个外键,两个外键分别关联另外的两张表

11.2、实体类中你的关系

1.继承关系
2.包含关系

orm框架分析步骤:

1.分析表与表之间的关系
2.确定表的关系(外键描述一对一,中间表描述多对多)
3.实体类中描述表关系(包含关系)
4.配置实体类和数据库表的映射关系

11.1一对多|多对一 案例:

员工和公司的关系
公司:一家公司
员工:这家公司的员工

一个员工对应一个公司,一个公司有多个员工

公司表为主表:
员工表为从表:

公司实体类(Company):一对多

/**
 * @author compass
 * @version 1.0
 * @date 2021-03-07 17:55
 */
@Entity()
@Table(name = "t_company")
public class Company  {

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

    @Column(name = "comp_Name")
    private String compName;

    @Column(name = "comp_email")
    private String compEmail;

    @Column(name = "comp_phone")
    private String compPhone;


    /**
     * 配置员工和公司的关系映射(一对多)
     *  @OneToMany: 一对多关系 targetEntity:对方对象的字节码(多的一方)
     *
     * 配置外键:
     * @JoinColumn : name :外键名称  referencedColumnName :外键的取值来源
     */
    @OneToMany(targetEntity = Employee.class)
    @JoinColumn(name = "comp_emp_id",referencedColumnName = "comp_id")
    private Set<Employee> companies = new HashSet();




    public Company() {
    }

    public Long getCompId() {
        return compId;
    }

    public void setCompId(Long compId) {
        this.compId = compId;
    }

    public String getCompName() {
        return compName;
    }

    public void setCompName(String compName) {
        this.compName = compName;
    }

    public String getCompEmail() {
        return compEmail;
    }

    public void setCompEmail(String compEmail) {
        this.compEmail = compEmail;
    }

    public String getCompPhone() {
        return compPhone;
    }

    public void setCompPhone(String compPhone) {
        this.compPhone = compPhone;
    }

    public Set<Employee> getCompanies() {
        return companies;
    }

    public void setCompanies(Set<Employee> companies) {
        this.companies = companies;
    }

    public Company(Long compId, String compName, String compEmail, String compPhone, Set<Employee> companies) {
        this.compId = compId;
        this.compName = compName;
        this.compEmail = compEmail;
        this.compPhone = compPhone;
        this.companies = companies;
    }


    @Override
    public String toString() {
        return "Company{" +
                "compId=" + compId +
                ", compName='" + compName + '\'' +
                ", compEmail='" + compEmail + '\'' +
                ", compPhone='" + compPhone + '\'' +
                ", companies=" + companies +
                '}';
    }
}

员工实体类(Employee):多对一

@Entity
@Table(name = "t_employee")
public class Employee {
    /**
     * 1.分析表与表之间的关系
     * 2.确定表的关系(外键描述一对一,中间表描述多对多)
     * 3.实体类中描述表关系(包含关系)
     * 4.配置实体类和数据库表的映射关系
     * 公司表为主表:
     * 员工表为从表:
     */

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "emp_id")

    private  Long empId;
    @Column(name = "emp_name")

    private String empName;

    @Column(name = "emp_phone")
    private String empPhone;

    @Column(name = "emp_job")
    private String empJob;

    /**
     * 配置多对一关系:
     * targetEntity:对方的字节码值
     *  @JoinColumn: name:外键名称  referencedColumnName:外键的去值来源
     */
    @ManyToOne(targetEntity = Company.class)
    @JoinColumn(name = "comp_emp_id",referencedColumnName = "comp_id")
    private Company company;


    public Long getEmpId() {
        return empId;
    }

    public void setEmpId(Long empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getEmpPhone() {
        return empPhone;
    }

    public void setEmpPhone(String empPhone) {
        this.empPhone = empPhone;
    }

    public String getEmpJob() {
        return empJob;
    }

    public void setEmpJob(String empJob) {
        this.empJob = empJob;
    }

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }



    public Employee(Long empId, String empName, String empPhone, String empJob, Company company) {
        this.empId = empId;
        this.empName = empName;
        this.empPhone = empPhone;
        this.empJob = empJob;
        this.company = company;
    }

    public Employee() {
    }

    @Override
    public String toString() {
        return "Employee{" +
                "empId=" + empId +
                ", empName='" + empName + '\'' +
                ", empPhone='" + empPhone + '\'' +
                ", empJob='" + empJob + '\'' +
                ", company=" + company +
                '}';
    }
}

插入数据:


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class OneToManyTest {
    @Autowired
    private CompanyRepository companyRepository;
    @Autowired
    private EmployeeRepository employeeRepository;

    /**
     * 此时的 保存一个员工到员工表中,是没有外键id的,两张表此时是独立存在的,员工的外键为空
     * 实体类中没有配置关系?
     */
    @Test
    @Transactional
    @Rollback(false)
    public  void TestAddEmployeeAndCompany(){

        Employee employee = new Employee();
        employee.setEmpJob("开发");
        employee.setEmpName("admin");
        employee.setEmpPhone("123456");

        Company company = new Company();
        company.setCompName("未知");
        company.setCompPhone("1234567");
        company.setCompEmail("weizhi@qq.com");

        // 解决方案1: Employee插入数据时没有外键
        // company.getCompanies().add(employee);
        
        // 解决方案2: Employee插入数据时没有外键
        Employee saveEmployee = employeeRepository.save(employee);
        System.out.println(saveEmployee);

        Company saveCompany = companyRepository.save(company);
        System.out.println(saveCompany);

    }

}

一对多的一方:放弃外键维护

    @OneToMany(mappedBy = "company")  //放弃外键维权 ,参照 company 的配置进行
    private Set<Employee> companies = new HashSet();

一对多的级联操作

    /**
     * 配置员工和公司的关系映射(一对多)
     *  @OneToMany: 一对多关系 targetEntity:对方对象的字节码(多的一方)
     *
     * 配置外键:
     * @JoinColumn : name :外键名称  referencedColumnName :外键的取值来源
     * mappedBy:对方的关系名称
     *
     * cascade : 配置级联(可以配置到设置多表的映射关系的注解上)
     *                   CascadeType.all         : 所有
     *                       MERGE       :更新
     *                     PERSIST     :保存
     *                      REMOVE      :删除
     */
    /*@OneToMany(targetEntity = Employee.class)
    @JoinColumn(name = "comp_emp_id",referencedColumnName = "comp_id")*/
    @OneToMany(mappedBy = "company",cascade = CascadeType.ALL)  //放弃外键维权 ,参照 company 的配置进行
    private Set<Employee> companies = new HashSet();

级联添加:

    //级联添加 (添加公司的时候,同时也把员工添加上,并且关联上外键)
    @Test
    @Transactional
    @Rollback(false)
    public  void cascadeInsertCompanyById(){


        Employee employee = new Employee();
        employee.setEmpJob("开发");
        employee.setEmpName("admin");
        employee.setEmpPhone("123456");

        Company company = new Company();
        company.setCompName("未知");
        company.setCompPhone("1234567");
        company.setCompEmail("weizhi@qq.com");

        company.getCompanies().add(employee);
        employee.setCompany(company);

        companyRepository.save(company);



    }

级联删除:

    //级联删除 (删除一个客户的时候,删除员工表中外键和客户表主键关联的数据)
    @Test
    @Transactional
    @Rollback(false)
    public  void cascadeDeleteCompanyById(){

       companyRepository.delete(13l);



    }
    

11.2多对多案例:

用户和角色的关系:

一个用户有多种角色
一个角色可以有多个用户

三个表:
一个用户表:sys_user :一个主键
一个角色表:sys_role : 存放sys_user和sys_role的主键,分别关联另外的两张表
中间表:sys_user_role: 一个主键

用户实体类:

/**
 * @author compass
 * @version 1.0 用户表
 * @date 2021-03-08 12:15
 */
@Entity
@Table(name = "sys_user")
public class User {

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

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

    @Column(name = "user_password")
    private String userPassword;

    /**
     * 配置用户到角色的多对多关系
     *   1.声明表关系
     *   2.描述表关系,配置中间表 (中间表包含两个外键)
     *   3. @ManyToMany:配置多对多关系  targetEntity : 对方的实体类字节码
     *   4. @JoinTable: name:中间表的名称
     *   5. @JoinTable: joinColumns:配置当前表在中间表的外键  inverseJoinColumns:对方对象在中间表的外键
     *          referencedColumnName:参照的是主表的主键名称
     *   6.cascade : 配置级联(可以配置到设置多表的映射关系的注解上)
     *
     *                        CascadeType.all         : 所有
     *                          MERGE       :更新
     *                         PERSIST     :保存
     *                          REMOVE      :删除
     *
     */
    @ManyToMany(targetEntity = Role.class)
    @JoinTable(name = "sys_user_role",
            joinColumns = {@JoinColumn(name = "sys__user_id",referencedColumnName = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "sys__role_id",referencedColumnName = "role_id")}
    )
    private Set<Role> roles = new HashSet<>();


    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserPassword() {
        return userPassword;
    }

    public void setUserPassword(String userPassword) {
        this.userPassword = userPassword;
    }

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

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

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", userPassword='" + userPassword + '\'' +
                ", roles=" + roles +
                '}';
    }
}

角色实体类:

/**
 * @author compass
 * @version 1.0 角色表
 * @date 2021-03-08 12:15
 */
@Entity
@Table(name = "sys_role")
public class Role {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "role_id")
    private Long roleId;

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

    //配置多对多
    @ManyToMany(targetEntity = User.class)
    @JoinTable(name = "sys_user_role",
            joinColumns = {@JoinColumn(name = "sys__role_id ",referencedColumnName = "role_id")},
            inverseJoinColumns = {@JoinColumn(name = "sys__user_id",referencedColumnName = " user_id")}
    )
   private Set<User> users = new HashSet<>();


    public Long getRoleId() {
        return roleId;
    }

    public void setRoleId(Long roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

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

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


    @Override
    public String toString() {
        return "Role{" +
                "roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                ", users=" + users +
                '}';
    }
}

测试保存,并且生成数据库表:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ManyToManyTest {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private RoleRepository  roleRepository;

    @Test
    @Transactional
    @Rollback(false)
    public void initTest(){

        User user = new User();
        user.setUserName("admin");
        user.setUserPassword("123");

        Role role = new Role();
        role.setRoleName("普通用户");

        userRepository.save(user);
        roleRepository.save(role);



    }
}

多对多,让其一方放弃维护权:


    //配置多对多
    @ManyToMany(mappedBy = "roles")  //让角色表放弃维护权
    private Set<User> users = new HashSet<>();

多对多级联操作:在User实体类中描述级联配置


    /**
     * 配置用户到角色的多对多关系
     *   1.声明表关系
     *   2.描述表关系,配置中间表 (中间表包含两个外键)
     *   3. @ManyToMany:配置多对多关系  targetEntity : 对方的实体类字节码
     *   4. @JoinTable: name:中间表的名称
     *   5. @JoinTable: joinColumns:配置当前表在中间表的外键  inverseJoinColumns:对方对象在中间表的外键
     *          referencedColumnName:参照的是主表的主键名称
     *   6.cascade : 配置级联(可以配置到设置多表的映射关系的注解上)
     *
     *                          CascadeType.all         : 所有
     *                          MERGE       :更新
     *                          PERSIST     :保存
     *                          REMOVE      :删除
     *
     */
    @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
    @JoinTable(name = "sys_user_role",
            joinColumns = {@JoinColumn(name = "sys__user_id",referencedColumnName = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "sys__role_id",referencedColumnName = "role_id")}
    )
    private Set<Role> roles = new HashSet<>();

级联添加:


    // 保存用户的时候同时保存用户的关联角色
    @Test
    @Transactional
    @Rollback(false)
    public void CascadeInsertTest(){

        User user = new User();
        user.setUserName("admin");
        user.setUserPassword("123");

        Role role = new Role();
        role.setRoleName("普通用户");

        /**
         * 报错 : 解决方案 :让其中一方 放弃维护权
         * 多对多放弃维护权,被动的一方放弃维护权
         * 让用户表去维护角色表,否则在重写toString 方法的时候会报异常
         */
        user.getRoles().add(role); //配置用户表到角色表之间的关系,可以对中间表进行数据维护 1-1
       // role.getUsers().add(user); //配置角色表到用户表之间的关系,可以对中间表进行数据维护 1-1

        User saveUser = userRepository.save(user);
        System.out.println(saveUser);


    }

级联删除:

    // 删除用户表中数据的时候同时删除中间表和角色表中的数据
    @Test
    @Transactional
    @Rollback(false)
    public void CascadeDeleteTest(){

        User user = userRepository.getOne(1l);
        userRepository.delete(user);

        // 数据由用户表管理 角色表不能删除数
       /* Role role = roleRepository.getOne(1l);
        roleRepository.delete(role);*/

    }

12.多表查询

一对多级联查询:

注意点:
如果两个实体类都重写了toString方法 会有问题 :stackOverflowError
解决方法:重写toString方法的时候,去掉关联的对象属性

延迟加载:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class MoreTableSelect {

    @Autowired
    private CompanyRepository companyRepository;

    @Autowired
    private EmployeeRepository employeeRepository;

    /**
     * 案例:客户和联系人
     * 多表查询
     * 对象导航查询:查询一个对象的同时,通过此对象查询他关联的对象
     * 错误:org.hibernate.LazyInitializationException: could not initialize proxy - no Session
     * 如果两个实体类都重写了toString方法 会有问题 :stackOverflowError 
     * 解决办法:重写toString方法的时候:去掉关联的对象属性
     */
    @Test
    @Transactional // 解决在java代码中的no session问题
    public  void objectNavigateSelectTest(){
        //查询 id为  1 的公司
        Company company = companyRepository.getOne(1l);

        //查询公司 id为 1 下面的所有员工
        List<Employee> employeeList = company.getCompanies();
        for (Employee employee : employeeList) {
            System.out.println(employee);
        }

    }
}

立即加载:

    @Test
    @Transactional // 解决在java代码中的no session问题
    public  void objectNavigateSelectGetOneTest(){
        //查询 id为  1 的公司
        Company company = companyRepository.findOne(1l); //立即加载

        //查询公司 id为 1 下面的所有员工
        List<Employee> employeeList = company.getCompanies();
        for (Employee employee : employeeList) {
            System.out.println(employee);
        }

    }
}

多对一级联查询:

    // 从员工对象中查询公司对象
    @Test
    @Transactional // 解决在java代码中的no session问题
    public  void objectNavigateSelectOneToMan(){
        
        //查询这个员工的同时,查询他所属的公司
        //默认还是延迟加载:要想立即加载:@OneToMany(mappedBy = "company")  中添加fetch属性中配置立即加载
        Employee employee = employeeRepository.getOne(3l);
        Company company = employee.getCompany();
        System.out.println(company);

    }

多表查询总结:

从一方查询多方:默认使用延迟加载:
原因:因为有时候我们只想需要一方的数据,但是如果使用立即加载,就会把多方的数据全部查出来,但是又没有用到就会极其的消耗性能。所以默认使用延迟加载

从多方查询一方:默认使用立即加载:

从多方查询一方的时候,因为只有一条数据,就好比一个员工对应一个公司一样,数据比较少,不会消耗太多性能,所有采用立即加载,如果是一方查询多方采用立即加载的话,就好比一个公司有上1000个员工,但是我们有时候并不需要马上查看这1000个员工的信息,所以先创建一个代理对象,等我们正在需要查着1000个员工的信息的时候,让这个代理对象去给我们查出来就好了,这样极大的提高了我们程序运行的性能。

13、完结散花

好了,到这里就完结散花了,学完SpringDataJpa之后和Mybatis做对比的话就很清楚了,SpringDataJpa是对象关系的映射,可以逆向生成数据库表,通过操作对象来操作数据库,而Mybatis是数据结果集的映射,而Mybatis-Plus是先有数据库表然后再生成实体类和属性,两者刚刚好是相反的。

两者的优缺点:

SpringDataJpa优点:

1.SpringDataJpa是对象的关系映射,所有我们只需要操作java对象即可操作数据库。基本的一些增删改查,可以不用手写,可以更加的注重业务逻辑。

2.因为SpringDataJpa是通过java对象去操作的数据库,所有不需要关注底层数据库,更换数据库的时候,只需要更改配置文件即可,扩展性强,移植性高。

SpringDataJpa缺点:

1.因为sql都是框架底层帮我们实现的,当业务逻辑非常复杂的时候,底层框架封装的sql可能会满足不了业务需求,而且sql是封装的,在进行sql调优的时候,不太容易。

Mybatis优点:

1.因为Mybatis是数据结果集的映射,帮我们封装好了结果集,我们只需要写sql就可以,因为sql都是自己写的,在实现业务逻辑的时候,可以更加灵活。

2.sql都是完全自己写的,没有经过底层封装,所有进行sql调优的时候,非常方便。

Mybatis缺点:

1.因为是手动写的sql去匹配的数据库,所以当我们更换底层数据库的时候,sql语句也会跟着变,所以扩展性,迁移性比较差。。

2.sql是手写的,所以很多简单的增删改查都需要我们自己手动去写。

以上就是我学完SpringDataJpa和Mybatis之后的总结,对于我们技术人员来说,其实技术没有什么好坏之分,也不是谁好谁坏,即存在,即合理,看的是开发场景,不同的开发场景,可能用到的技术会不同,所有技术有时候是根据开发场景去进行匹配的。

标签:customer,entityManager,String,JPA,void,查询,public,springData
来源: https://blog.csdn.net/m0_46188681/article/details/114358310