ORM概述
##ORM思想
主要目的:操作实体类就相当于操作数据库表
建立两个映射关系:

实体类和表的映射关系
实体类中属性和表中字段的映射关系

不再重点关注sql语句

为什么使用ORM
当实现一个应用程序时(不使用O/R Mapping),我们可能会写特别多数据访问层的代码,从数据库保存数据、修改数据、删除数据,而这些代码都是重复的。而使用ORM则会大大减少重复性代码。对象关系映射(Object Relational Mapping,简称ORM),主要实现程序对象到关系数据库数据的映射
##常见的ORM矿建
实现了ORM思想的框架:MyBatis,Hibernate、Jpa

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

JPA规范
jpa规范,实现jpa规范,内部是由接口和抽象类组成
##JPA与Hibernate的关系
SpringData - 图6

JPA的基本操作
案例:客户的相关操作(增删改查)
客户:就是一家公司
客户表:
jpa操作的操作步骤:
1.加载配置文件创建实体管理器工厂

2.根据实体管理器工厂,创建实体管理器

3.创建事务对象,开启事务

4.增删改查操作
5.提交事务
6.释放资源
搭建环境的过程
1.创建maven工程导入坐标

  1. <dependencies>
  2. <!--c3p0-->
  3. <dependency>
  4. <groupId>com.mchange</groupId>
  5. <artifactId>c3p0</artifactId>
  6. <version>0.9.5.5</version>
  7. </dependency>
  8. <!--Junit-->
  9. <dependency>
  10. <groupId>junit</groupId>
  11. <artifactId>junit</artifactId>
  12. <version>4.13.2</version>
  13. <scope>test</scope>
  14. </dependency>
  15. <!--hibernate对Jpa的支持-->
  16. <dependency>
  17. <groupId>org.hibernate</groupId>
  18. <artifactId>hibernate-entitymanager</artifactId>
  19. <version>5.0.7.Final</version>
  20. </dependency>
  21. <!--Log日志-->
  22. <dependency>
  23. <groupId>log4j</groupId>
  24. <artifactId>log4j</artifactId>
  25. <version>1.2.17</version>
  26. </dependency>
  27. <!--MySQL and MariaDB-->
  28. <dependency>
  29. <groupId>mysql</groupId>
  30. <artifactId>mysql-connector-java</artifactId>
  31. <version>8.0.26</version>
  32. </dependency>
  33. </dependencies>

2.需要配置Jpa的核心配置文件
位置:配置到类路径下一个叫做MATA-INF的文件夹下
命名:persistence.xml
SpringData - 图7

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
  3. <!--需要配置persistent-unit节点
  4. 持久化单元:
  5. name:持久化单元名称
  6. transaction-type:事务管理的方式
  7. JTA:分布式事务管理
  8. RESOURCE-LOCAL:本地事务管理
  9. -->
  10. <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
  11. <!--JPA的实现方式-->
  12. <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
  13. <!--可选配置:配置JPA实现方的配置信息-->
  14. <properties>
  15. <!--数据库信息
  16. 用户名,密码,驱动,数据库地址
  17. -->
  18. <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
  19. <property name="javax.persistence.jdbc.url" value="jdbc:mysql:///springdata"/>
  20. <property name="javax.persistence.jdbc.user" value="root"/>
  21. <property name="javax.persistence.jdbc.password" value="ced51515151"/>
  22. <!--配置JPA实现方(hibernate)的配置信息
  23. 显示sql : true | false
  24. 自动创建数据库表 : hibernate.hbm2ddl.auto
  25. create : 程序运行时创建数据库表(如果有表,先删除再创建)
  26. update :程序运行时创建数据库表(如果有表,不会创建表)
  27. none :不会创建表
  28. -->
  29. <property name="hibernate.show_sql" value="true"/>
  30. <property name="hibernate.hbm2ddl.auto" value="create"/>
  31. </properties>
  32. </persistence-unit>
  33. </persistence>

3.编写客户的实体类
4.配置实体类和表,类中属性和表中字段的映射关系

  1. package com.cedric.domain;
  2. import javax.persistence.*;
  3. /**
  4. * 客户实体类
  5. * 配置映射关系:
  6. * 1.实体类和表的映射关系
  7. * 2.实体类中的属性和表中字段的映射关系
  8. *
  9. * @Entity : 声明实体类
  10. * @Table : 配置实体类和表的映射关系
  11. * name : 配置数据库表的名称
  12. */
  13. @Entity
  14. @Table(name = "cst_customer")
  15. public class Customer {
  16. /**
  17. * @Id : 声明主键的配置
  18. * @GeneratedValue:配置主键的生成策略
  19. * strategy
  20. * GenerationType.IDENTITY:自增,MySQL
  21. * * 底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增)
  22. * GenerationType.SEQUENCE:序列,Oracle
  23. * * 底层数据库必须支持序列
  24. * GenerationType.TABLE : jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
  25. * GenerationType.AUTO : 由程序自动帮助我们选择主键生成策略
  26. * @Column:配置属性和字段的映射关系
  27. * name:数据库表中的字段名称
  28. */
  29. @Id
  30. @GeneratedValue(strategy = GenerationType.IDENTITY)
  31. @Column(name = "cust_id")
  32. private Long custId; //客户的主键
  33. @Column(name = "cust_name")
  34. private String custName;//客户姓名
  35. @Column(name = "cust_source")
  36. private String custSource;//客户来源
  37. @Column(name = "cust_level")
  38. private String custLevel;//客户级别
  39. @Column(name = "cust_industry")
  40. private String custIndustry;//客户所属行业
  41. @Column(name = "cust_phone")
  42. private String custPhone;//客户联系方式
  43. @Column(name = "cust_address")
  44. private String custAddress;//客户地址
  45. // setter and getter
  46. // toString
  47. }

5.保存客户到数据库中

package com.cedric.test;

import com.cedric.domain.Customer;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class JpaTest {

    /**
     * 测试Jpa的保存
     *      案例:保存一个客户到数据库中
     *  Jpa的操作步骤
     *      1.加载配置文件创建工厂(实体管理类工厂)对象
     *      2.通过实体管理类工厂获取实体管理器
     *      3.获取事务对象,开启事务
     *      4.完成增删改查操作
     *      5.提交事务(回滚事务)
     *      6.释放资源
     */

    @Test
    public void testSave(){
        //1.加载配置文件创建工厂(实体管理类工厂)对象
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
        //2.通过实体管理类工厂获取实体管理器
        EntityManager entityManager = factory.createEntityManager();
        //3.获取事务对象,开启事务
        EntityTransaction transaction = entityManager.getTransaction();//获取事务对象
        transaction.begin();//开启事务
        //4.完成增删改查操作:保存一个客户到数据库中
        Customer customer = new Customer();
        customer.setCustName("张三");
        customer.setCustIndustry("国防");
        customer.setCustAddress("北京");
        customer.setCustLevel("SVIP");
        customer.setCustPhone("111");
        //保存
        entityManager.persist(customer);
        //5.提交事务(回滚事务)
        transaction.commit();
        //6.释放资源
        entityManager.close();
        factory.close();
    }
}

ii.完成基本的CRUD案例
persist : 保存
merge : 更新
remove : 删除
find/getReference : 根据id查询

抽取JPAUtil工具类

package com.cedric.utils;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

/**
 * 解决实体管理器工厂的浪费资源问题和耗时问题
 *      通过静态代码块的形式,当程序第一次访问此工具类时,创建一个公共的实体管理器工厂对象
 *
 * 第一次访问getEntityManager方法:经过静态代码块创建一个factory对象,再调用方法创建一个EntityManager对象
 * 第二次访问getEntityManager方法:直接通过一个已经创建好的factory对象,创建EntityManager对象
 */
public class JpaUtils {

    private static EntityManagerFactory factory;

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

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

使用JPA完成 增删改查操作
##保存

  @Test
    public void testSave(){
        //1.加载配置文件创建工厂(实体管理类工厂)对象
        //EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
        //2.通过实体管理类工厂获取实体管理器
        //EntityManager entityManager = factory.createEntityManager();


        EntityManager entityManager = JpaUtils.getEntityManager();
        //3.获取事务对象,开启事务
        EntityTransaction transaction = entityManager.getTransaction();//获取事务对象
        transaction.begin();//开启事务
        //4.完成增删改查操作:保存一个客户到数据库中
        Customer customer = new Customer();
        customer.setCustName("张三");
        customer.setCustIndustry("国防");
        customer.setCustAddress("北京");
        customer.setCustLevel("SVIP");
        customer.setCustPhone("111");
        //保存
        entityManager.persist(customer);
        //5.提交事务(回滚事务)
        transaction.commit();
        //6.释放资源
        entityManager.close();
        //factory.close();
    }

修改

 /**
     * 更新客户的操作
     *      merge(Object)
     */
    @Test
    public void testUpdate(){
        //1.通过工具类获取entityManager
        EntityManager entityManager = JpaUtils.getEntityManager();
        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //3.增删改查 -- 更新操作

        //i.查询客户
        Customer customer = entityManager.find(Customer.class, 1l);
        //ii.更新客户
        customer.setCustIndustry("线上营销");
        entityManager.merge(customer);

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

删除

  @Test
    public void testRemove(){
        //1.通过工具类获取entityManager
        EntityManager entityManager = JpaUtils.getEntityManager();
        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //3.增删改查 -- 删除客户

        // i.根据id查询客户
        Customer customer = entityManager.find(Customer.class, 1l);
        // ii:调用remove方法完成删除操作
        entityManager.remove(customer);

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

查询

  /**
     * 根据id查询用户
     *   使用find方法查询:
     *      1.查询的对象就是当前客户对象本身
     *      2.在调用find方法的时候,就会发送sql语句查询数据库
     *
     *  立即加载
     */
    @Test
    public void testFind(){
        //1.通过工具类获取entityManager
        EntityManager entityManager = JpaUtils.getEntityManager();
        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //3.增删改查 -- 根据id查询用户
        /**
         * find:根据id查询数据
         *      class:查询数据的结果需要包装的实体类类型的字节码
         *      id:查询的主键的取值
         */
        Customer customer = entityManager.find(Customer.class, 1l);
        System.out.println(customer);
        //4.提交事务
        transaction.commit();
        //5.释放资源
        entityManager.close();
    }
 /**
     * 根据id查询客户
     *      getReference方法
     *          1.获取的对象是一个动态代理对象
     *          2.调用getReference方法不会立即发送sql语句查询数据库
     *              * 当调用查询结果对象的时候,才会发送查询的sql语句:什么时候用,什么时候发送sql语句查询数据库
     *  延迟加载(懒加载 )
     *      * 得到的是一个动态代理对象
     *      * 什么时候用,什么时候才会查询
     */
    @Test
    public void testReference(){
        //1.通过工具类获取entityManager
        EntityManager entityManager = JpaUtils.getEntityManager();
        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //3.增删改查 -- 根据id查询用户
        /**
         * getReference:根据id查询数据
         *      class:查询数据的结果需要包装的实体类类型的字节码
         *      id:查询的主键的取值
         */
        Customer customer = entityManager.getReference(Customer.class, 1l);
        System.out.println(customer);
        //4.提交事务
        transaction.commit();
        //5.释放资源
        entityManager.close();
    }

jpql查询

复杂查询
###查询全部

 /**
     * 查询全部
     *      jqpl:from Customer
     *      sql:SELECT * FROM cst_customer
     */
    @Test
    public void testFindAll(){
        //1.获取entityManager对象
        EntityManager entityManager = JpaUtils.getEntityManager();
        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //3.查询全部
        String jpql = "from Customer";
        Query query = entityManager.createQuery(jpql);

        // 发送查询,封装结果集
        List list = query.getResultList();

        for(Object obj : list){
            System.out.println(obj);
        }
        //4.提交事务
        transaction.commit();
        //5.释放资源
        entityManager.close();
    }

排序查询

 /**
     * 排序查询:倒叙查询所有客户(根据 id倒序)
     *      sql:   SELECT * FROM cst_customer ORDER BY cust_id DESC
     *      jqpl:  from Customer order by custId desc
     *
     * 进行jpql查询
     *      1.创建query对象
     *      2.对参数进行赋值
     *      3.查询并得到返回结果
     */
    @Test
    public void testOrders(){
        //1.获取entityManager对象
        EntityManager entityManager = JpaUtils.getEntityManager();
        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //3.查询全部
        String jpql = "from Customer order by custId desc";
        Query query = entityManager.createQuery(jpql);

        // 发送查询,封装结果集
        List list = query.getResultList();

        for(Object obj : list){
            System.out.println(obj);
        }
        //4.提交事务
        transaction.commit();
        //5.释放资源
        entityManager.close();
    }

查询总数

  @Test
    public void testCount(){
        //1.获取entityManager对象
        EntityManager entityManager = JpaUtils.getEntityManager();
        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //3.查询全部
        //i.根据jpql语句创建查询对象
        String jpql = "select count(custId) from Customer";
        Query query = entityManager.createQuery(jpql);
        //ii.对参数赋值
        //iii.发送查询,封装结果集
        /**
         * getResultList:直接将查询结果封装为list集合
         * getSingleResult:得到唯一的结果集
         */
        Object singleResult = query.getSingleResult();
        System.out.println(singleResult);
        //4.提交事务
        transaction.commit();
        //5.释放资源
        entityManager.close();
    }

分页查询

/**
     * 分页查询
     *      sql  :  select * from cst_customer limit 0 2
     *      jpql :  from Customer
     */
    @Test
    public void testPaged(){
        //1.获取entityManager对象
        EntityManager entityManager = JpaUtils.getEntityManager();
        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //3.查询全部
        //i.根据jpql语句创建查询对象
        String jpql = "from Customer";
        Query query = entityManager.createQuery(jpql);
        //ii.对参数赋值 -- 分页查询
        query.setFirstResult(0);
        query.setMaxResults(2);
        //iii.发送查询,封装结果集
        /**
         * getResultList:直接将查询结果封装为list集合
         * getSingleResult:得到唯一的结果集
         */
        List list = query.getResultList();
        for (Object obj : list){
            System.out.println(obj);
        }
        //4.提交事务
        transaction.commit();
        //5.释放资源
        entityManager.close();
    }

条件查询

 /**
     * 条件查询
     *      案例:查询客户名称以“腾讯”开头的客户
     *          sql : SELECT * FROM cst_customer WHERE cust_name LIKE ?
     *          jpql:  FROM Customer WHERE custName like ?
     */
    @Test
    public void testCondition(){
        //1.获取entityManager对象
        EntityManager entityManager = JpaUtils.getEntityManager();
        //2.开启事务
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //3.查询全部
        //i.根据jpql语句创建查询对象
        String jpql = "from Customer where custName like ?";
        Query query = entityManager.createQuery(jpql);
        //ii.对参数赋值 -- 占位符参数
        //第一个参数:占位符的索引位置(从1)开始,第二个参数:取值
        query.setParameter(1,"腾讯%");
        //iii.发送查询,封装结果集
        /**
         * getResultList:直接将查询结果封装为list集合
         * getSingleResult:得到唯一的结果集
         */
        List list = query.getResultList();
        for (Object obj : list){
            System.out.println(obj);
        }
        //4.提交事务
        transaction.commit();
        //5.释放资源
        entityManager.close();
    }

SpringDataJpa的概述
##SpringDataJpa概述
SpringDataJpa是Spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据库的访问和。他提供了包括增删改查在内的常用功能,且易于扩展,学习并使用SpringDataJpa可以极大提高开发效率
##SpringDataJpa的特性
SpringDataJpa极大简化了数据库访问层代码。
使用了SpringDataJpa,我们的dao层中只需要写接口,就自动具有了增删改查、分页查询等方法
##SpringDataJpa与JPA和hibernate之间的关系
SpringData - 图8
SpringDataJpa是Spring提供的一套对JPA操作更加高级的封装,是在JPA规范下专门用来进行数据持久化的解决方案
#SpringDataJpa入门
##需求说明
对客户进行CRUD
##搭建SpringDataJpa的开发环境
创建工程导入坐标
配置spring的配置文件
编写实体类(Customer),使用jpa注解配置映射关系
II.编写一个符合SpringDataJpa的dao层接口
###引入SpringDataJpa的坐标

 <properties>
        <spring.version>5.0.2.RELEASE</spring.version>
        <hibernate.version>5.0.7.Final</hibernate.version>
        <slf4j.version>1.6.6</slf4j.version>
        <log4j.version>1.2.12</log4j.version>
        <c3p0.version>0.9.1.2</c3p0.version>
        <mysql.version>8.0.26</mysql.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- junit单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</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>

        <!-- spring对orm框架的支持包-->
        <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>log4j</groupId>
            <artifactId>log4j</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>

        <!-- spring data jpa 的坐标-->
        <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 -->
    </dependencies>

整合SpringDataJpa与Spring

<?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"/>
        <!--配置扫描的包(实体类所在的包)-->
        <property name="packagesToScan" value="com.cedric.domain"/>
        <!--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" />
                <!--是否显示sql -->
                <property name="showSql" value="true" />
            </bean>
        </property>

        <!--jpa的方言 : 高级的特性-->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
        </property>
    </bean>

    <!--2.创建数据库连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="root"></property>
        <property name="password" value="cedricasdsaca"></property>
        <property name="jdbcUrl" value="jdbc:mysql:///springdata"></property>
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
    </bean>

    <!--3.整合spring Data Jpa-->
    <jpa:repositories base-package="com.cedric.dao" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityManagerFactory">
    </jpa:repositories>

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

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

    <!--6.配置包扫描-->
    <context:component-scan base-package="com.cedric"></context:component-scan>
</beans>

使用JPA注解配置映射关系

package com.cedric.domain;

import javax.persistence.*;

/**
 * 1.实体类和表的映射关系
 *      @Entity
 *      @Table
 * 2.类中属性和表中字段的映射关系
 *      @Id
 *      @GeneratedValue
 *      @Column
 */
@Entity
@Table(name = "cst_customer")
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cust_id")
    private Long custId;
    @Column(name = "cust_address")
    private String custAddress;
    @Column(name = "cust_industry")
    private String custIndustry;
    @Column(name = "cust_level")
    private String custLevel;
    @Column(name = "cust_name")
    private String custName;
    @Column(name = "cust_phone")
    private String custPhone;
    @Column(name = "cust_source")
    private String custSource;

    // set and get
    // toString

使用SpringDataJpa完成需求

package com.cedric.dao;

import com.cedric.domain.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

/**
 * 符合SpringData的dao层的接口规范
 *      JpaRepository<操作的实体类型,实体类中主键的类型>
 *             * 封装了基本CRUD操作
 *      JpaSpecificationExecutor<操作的实体类型>
 *              * 封装了复杂查询(分页)
 */
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
}
package com.cedric.test;

import com.cedric.dao.CustomerDao;
import com.cedric.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class) //声明spring提供的单元测试
@ContextConfiguration(locations = "classpath:applicationContext.xml") //指定spring容器的配置信息
public class CustomerDaoTest {

    @Autowired
    private CustomerDao customerDao;

    /**
     * 根据id查询
     */
    @Test
    public void testFindOne(){
        Customer customer = customerDao.findOne(3l);
        System.out.println(customer);
    }

    /**
     * save:保存或更新
     *      根据传递的对象是否存在主键id,
     *      如果没有id主键属性:保存
     *      存在id主键属性,根据id查询数据,更新数据
     */
    @Test
    public void testSave(){
        Customer customer = new Customer();
        customer.setCustName("农夫山泉");
        customer.setCustLevel("Vip");
        customer.setCustIndustry("营销");
        customerDao.save(customer);
    }

    @Test
    public void testUpdate(){
        Customer customer = new Customer();
        customer.setCustId(4l);
        customer.setCustName("农夫山泉有点甜");
        customerDao.save(customer);
    }

    @Test
    public void testDelete(){
        customerDao.delete(5l);
    }

    /**
     * 查询所有
     */
    @Test
    public void testFindAll(){
        List<Customer> list = customerDao.findAll();
        for (Customer customer : list){
            System.out.println(customer);
        }
    }
}

小结

SpringDataJpa的运行过程和原理剖析
SpringData - 图9
SpringData - 图10
1.通过JdkDynamicAopProxy的invoke方法创建了一个动态代理对象
2.SimpleJpaRepository当中封装了JPA的操作(借助JPA的api完成数据库的CRUD)
3.通过hibernate完成数据库的操作(封装jdbc)

复杂查询
##借助接口中定义好的方法完成查询
findOne(id) : 根据Id查询

  @Test
    public void testFindOne(){
        Customer customer = customerDao.findOne(3l);
        System.out.println(customer);
    }
    /**
     * 测试统计查询:查询客户的总数量
     *      count:统计总条数
     */
    @Test
    public void testCount(){
        long count = customerDao.count();//查询全部的客户数量
        System.out.println(count);
    }
    /**
     * 测试:判断id为4的客户是否存在
     *      1.可以查询一下id为4的客户
     *          如果值为空,代表不存在在,如果不为空,代表存在
     *      2.判断数据库中id为4的客户的数量
     *          如果数量为0,代表不存在,如果大于0,代表存在
     */
    @Test
    public void testExists(){
        boolean exists = customerDao.exists(4l);
        System.out.println("id为4的客户是否存在:" + exists);
    }
    /**
     * 根据id从数据库中查询
     *    @Transactional  :  保证getOne正常运行
     *
     *    findOne:
     *      em.find()      :立即加载
     *    getOne:
     *      em.getReference     :延迟加载
     *      * 返回的是一个客户的动态代理对象
     *      * 什么时候用,什么时候查询
     */
    @Test
    @Transactional
    public void testGetOne(){
        Customer customer = customerDao.getOne(4l);
        System.out.println(customer);
    }

jpql的查询方式
jpql : jpa query language(jpa查询语言)
特点:语法或关键字和sql语句类似
查询的是类和类中的属性

需要将JPQL语句配置到接口方法上
1.特有的查询:需要在dao接口上配置方法
2.在新添加的方法上,使用注解的形式配置jpql查询语句
3.注解 : @Query

jpql代码实例


/**
 * 符合SpringData的dao层的接口规范
 *      JpaRepository<操作的实体类型,实体类中主键的类型>
 *             * 封装了基本CRUD操作
 *      JpaSpecificationExecutor<操作的实体类型>
 *              * 封装了复杂查询(分页)
 */
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {

    /**
     * 案例:根据客户名称查询客户
     *
     * jpql:from Customer where custName = ?
     *
     * 配置jpql语句,使用@Query注解
     */
    @Query(value = "from Customer where custName = ?")
    Customer findJpql(String custName);

    /**
     * 案例:根据客户名称和客户id查询客户
     *      jpql:from Customer where custName = ? and custId = ?
     *
     * 可以指定占位符参数的位置
     *      ? 索引的方式,指定此占位的取值来源
     */
    @Query(value = "from Customer  where custName = ?2 and custId = ?1")
    Customer findCustNameAndId(Long id,String name);

    /**
     * 使用jpql完成更新操作
     *      案例:根据id更新客户名称
     *          更新4号客户名称,将名称改为”农夫山泉“
     *  sql:update cst_customer set cust_name = ? where cust_id = ?
     *  jpql:update Customer set custName=? where custId = ?
     *
     * @Query : 代表的是进行查询
     *      * 声明此方法使用了做更新操作
     * @Modifying
     *      * 当前执行的是一个更新操作
     */
    @Query(value = "update Customer set  custName = ?2 where custId = ?1")
    @Modifying
    void updateCustomer(long custId,String name);


    /**
     * 使用sql的形式查询:
     *      查询全部的客户
     * sql:select * from cst_customer
     * Query:配置sql查询
     *      value : sql语句
     *      nativeQuery : 查询方式
     *          true : sql查询
     *          false : jpql查询
     *
     * @return
     */
    //@Query(value = "select * from cst_customer",nativeQuery = true)
    @Query(value = "select * from cst_customer where cust_name like ?1",nativeQuery = true)
    List<Object []> findSql(String name);


    /**
     *  通过方法名称规则查询
     *  是对jpql查询更深入的一层封装
     *  我们只需要按照SpringDataJpa提供的方法名称规则定义方法,不需要再去配置jpql语句,完成查询
     *
     *  findBy开头:代表查询
     *      对象中属性的名称(首字母大写)
     *   * 含义:根据属性名称进行查询
     *
     *   findByCustName -- 根据客户名称查询
     *
     *   在springDataJpa的运行阶段
     *          会根据方法名进行解析  findBy  from    xxx(实体类)
     *                              属性名称    where custName =
     *    * 默认情况 : 使用等于的方式查询
     *          特殊的查询方式
     *
     *     1. findBy + 属性名称(根据属性名称进行完成匹配的查询)
     *     2. findBy + 属性名称 + ”查询方式(Like | is null)“
     *          findByCustNameLike
     *     3.多条件查询
     *          findBy + 属性名 + ”查询方式" + “多条件的连接符(and | or)” + 属性名 + “查询方式”
     */
    Customer findByCustName(String name);

    List<Customer> findByCustNameLike(String custName);

    //使用客户名称模糊匹配和客户所属行业精准匹配的查询
    Customer findByCustNameLikeAndCustIndustry(String name,String custIndustry);
}
package com.cedric.test;

import com.cedric.dao.CustomerDao;
import com.cedric.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class JpqlTest {
    @Autowired
    private CustomerDao customerDao;

    @Test
    public void testFindJPQL(){
        Customer customer = customerDao.findJpql("京东");
        System.out.println(customer);
    }

    @Test
    public void testFindCustNameAndId(){
        Customer customer = customerDao.findCustNameAndId(3l,"京东");
        System.out.println(customer);
    }

    /**
     * 测试jpql更新操作
     *      * springDataJpa中使用jpql完成 更新/删除操作
     *              * 需要手动添加事务的支持
     *              * 默认会执行结束之后,回滚事务
     *
     *   @Rollback  :  设置是否自动回滚
     */
    @Test
    @Transactional // 添加事务的支持
    @Rollback(value = false)
    public void testUpdateCustomer(){
        customerDao.updateCustomer(4l,"农夫山泉");
    }

    // 测试sql查询
    @Test
    public void testFindSql(){
        List<Object[]> list = customerDao.findSql("阿里%");
        for (Object[] obj : list){
            System.out.println(Arrays.toString(obj));
        }
    }

    @Test
    public void testNaming(){
        Customer customer = customerDao.findByCustName("京东");
        System.out.println(customer);
    }

    // 测试方法命名规则的查询
    @Test
    public void testByCustNameLike(){
        List<Customer> list = customerDao.findByCustNameLike("阿里%");
        for (Customer customer : list){
            System.out.println(customer);
        }
    }

    @Test
    public void testFindByNameLikeAndCustIndustry(){
        Customer customer = customerDao.findByCustNameLikeAndCustIndustry("阿里%", "电商");
        System.out.println(customer);
    }
}

Specifications动态查询
##方法列表

     T findOne(Specification<T> var1); //查询单个对象

    List<T> findAll(Specification<T> var1);//查询列表

    // 查询全部,分页
    // pageable:分页参数
    // 返回值:分页pageBean(page:是springdataajpa提供的)
    Page<T> findAll(Specification<T> var1, Pageable var2);

    //查询列表
    //Sort:排序参数
    List<T> findAll(Specification<T> var1, Sort var2);

    long count(Specification<T> var1);//统计查询

Specification : 查询条件
自定义我们自己的Specification实现类
实现
Root:查询的根对象(查询的任何属性都可以从根对象中获取)
CriteriaQuery:顶层查询对象,自定义查询方式(了解:一般不用)
CriteriaBuilder:查询的构造器,封装了很多的查询条件
Predicate toPredicate(Root var1, CriteriaQuery<?> var2, CriteriaBuilder var3); //封装查询条件

代码实例

package com.cedric.test;

import com.cedric.dao.CustomerDao;
import com.cedric.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.persistence.criteria.*;
import java.util.List;

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

    @Autowired
    private CustomerDao customerDao;

    /**
     * 根据条件,查询单个对象
     */
    @Test
    public void testSpec(){
        /**
         * 自定义查询条件
         *  1.实现Specification接口(提供泛型,查询的对象类型)
         *  2.实现toPredicate方法(构造查询条件)
         *  3.需要借助方法参数中的两个参数(
         *      root:获取需要查询的对象属性
         *      CriteriaBuilder:构造查询条件的内部封装了很多查询条件(模糊匹配,精准匹配)
         *  )
         *
         *  案例:根据客户名称查询,查询客户名为京东的客户
         *      查询条件
         *          1.查询方式
         *              cb对象
         *          2.比较的属性名称
         *              root对象
         */
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                // 1.获取比较的属性
                Path<Object> custName = root.get("custName");
                // 2.构造查询条件     :   select * from cst_customer where cust_name = '京东'
                /**
                 * 第一个参数:需要比较的属性(path对象)
                 * 第二个参数:当前需要比较的取值
                 */
                Predicate predicate = criteriaBuilder.equal(custName, "京东");//进行精准匹配(比较的属性,比较的属性取值)
                return predicate;
            }
        };
        Customer customer = customerDao.findOne(spec);
        System.out.println(customer);
    }

    /**
     * 多条件查询
     *      案例:根据客户名(腾讯)和客户所属行业查询(网游)
     */
    @Test
    public void testSpec1(){
        /**
         * root:获取属性
         *      客户名
         *      所属行业
         * criteriaBuilder:构造查询
         *      1.构造客户名的精准匹配查询
         *      2.构造所属行业的精准匹配查询
         *      3.将以上两个查询联系起来
         */
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Path<Object> custName = root.get("custName");//客户名
                Path<Object> custIndustry = root.get("custIndustry");//所属行业

                //构造函数
                //1.构造客户名的精准匹配查询
                Predicate predicate = criteriaBuilder.equal(custName, "腾讯");// 第一个参数,path(属性),第二个参数,属性的取值
                //2.构造所属行业的精准匹配查询
                Predicate predicate1 = criteriaBuilder.equal(custIndustry, "网游");
                //3.将多个查询条件组合到一起:组合(满足条件一并且满足条件二:与关系,满足条件一或满足条件二即可:或关系)
                Predicate and = criteriaBuilder.and(predicate, predicate1);//以与的形式拼接多个查询条件
                //criteriaBuilder.or();//以或的形式拼接多个查询条件
                return and;
            }
        };
        Customer customer = customerDao.findOne(spec);
        System.out.println(customer);
    }

    /**
     * 案例:完美根据客户名称的模糊匹配,返回客户列表
     *      客户名称以‘阿里’开头
     * equal:直接得到path对象(属性),然后进行比较即可
     * gt,lt,ge,le,like:得到path对象,根据path指定比较的参数类型,再去进行比较
     *      指定参数类型:path.as(类型的字节码对象)
     */
    @Test
    public void testSpec3(){
        //构造查询条件
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //查询属性:用户名
                Path<Object> custName = root.get("custName");
                //查询方式:模糊匹配
                Predicate like = criteriaBuilder.like(custName.as(String.class), "阿里%");
                return like;
            }
        };
       /* List<Customer> all = customerDao.findAll(spec);
        for(Customer customer : all){
            System.out.println(customer);
        }*/

        //添加排序
        //创建排序对象,需要调用构造方法实例化sort对象
        //第一个参数:排序的顺序(倒序,正序)
        //Sort.Direction.DESC:倒序
        //Sort.Direction.ASC:升序
        //第二个参数:排序的属性名称
        Sort sort = new Sort(Sort.Direction.DESC,"custId");
        List<Customer> all = customerDao.findAll(spec, sort);
        for(Customer customer : all){
            System.out.println(customer);
        }
    }

    /**
     * 分页查询
     *      Specification:查询条件
     *      Pageable:分页参数
     *          分页参数:查询的页码,每页查询的条数
     *      findAll(Specification,Pageable):带有条件的分页
     *      findAll(Pageable):没有条件的分页
     *  返回:Page(SpringDataJpa为我们封装好的pageBean对象,数据列表,总条数)
     */
    @Test
    public void testSpec4(){

        Specification spec = null;
        //PageRequest对象是Pageable接口的实现类
        /**
         * 创建PageRequest的过程中,需要调用他的构造方法传入两个参数
         *      第一个参数:当前查询的页数(从0开始)
         *      第二个参数:每页查询的数量
         */
        Pageable pageable = new PageRequest(0,2);
        // 分页查询
        Page<Customer> page = customerDao.findAll(null, pageable);
        System.out.println(page.getContent());//得到数据集合列表
        System.out.println(page.getTotalElements());//得到总条数
        System.out.println(page.getTotalPages());//得到总页数
    }
}


###一对多操作

####添加

````java
@Autowired
private CustomerDao customerDao;

@Autowired
private LinkManDao linkManDao;

/**
 * 保存一个客户,保存一个联系人
 *
 * 注意:实体类中需要配置关系,否则外键为空
 */
@Test
@Transactional  //配置事务
@Rollback(value = false)//不自动回滚
public void testAdd(){
    // 创建一个客户,创建一个联系人
    Customer customer = new Customer();
    customer.setCustName("火狐");

    LinkMan linkMan = new LinkMan();
    linkMan.setLkmName("Jack");

    /**
     * 配置了客户到联系人的关系
     *      从客户的角度:发送两条insert语句,发送一条更新语句更新数据库(更新外键)
     * 由于配置了客户到联系人的关系:客户可以对外键进行维护
     */
    customer.getLinkMans().add(linkMan);

    customerDao.save(customer);
    linkManDao.save(linkMan);
}

@Test
@Transactional  //配置事务
@Rollback(value = false)//不自动回滚
public void testAdd1(){
    // 创建一个客户,创建一个联系人
    Customer customer = new Customer();
    customer.setCustName("火狐");

    LinkMan linkMan = new LinkMan();
    linkMan.setLkmName("Jack");

    /**
     * 配置联系人到客户的关系(多对一)
     *      只发送了两条insert语句
     * 由于配置了联系人到客户的映射关系(多对一)
     */
    linkMan.setCustomer(customer);

    customerDao.save(customer);
    linkManDao.save(linkMan);
}

@Test
@Transactional  //配置事务
@Rollback(value = false)//不自动回滚
public void testAdd2(){
    // 创建一个客户,创建一个联系人
    Customer customer = new Customer();
    customer.setCustName("火狐");

    LinkMan linkMan = new LinkMan();
    linkMan.setLkmName("Jack");

    linkMan.setCustomer(customer);//由于配置了多的一方到一的一方的关联关系(当保存的时候,就已经对外键赋值)
    customer.getLinkMans().add(linkMan);//由于配置了多的一方到一的一方的关联关系(发送一条update语句)

    customerDao.save(customer);
    linkManDao.save(linkMan);
}