springDataJpa初识总结

JPA基础知识

传统jdbc存在的问题?

在这里插入图片描述

  • 操作繁琐
    解决方案: 封装工具类
  • 占位符赋值麻烦
    解决方案:
    *思路: 我么能不能通过面向对象的方法进行封装呢?达到封装好之后sava(Object obj) 的方法
    *思路延伸: 我们能不能自动拼接sql呢?只要完成自动拼接,我们就实现了上面的想法
    *通过分析:我们发现,只要能
    1. 建立实体类之间的对应关系
    2. 简历实体类属性和表字段之间的关系
      这样我们就能够拼接处sql达到封装的目的
  • 上述简历实体类对象和数据库表之间的映射关系,我们就叫作对象-关系-映射(Object Relational Mapping) 简称 ORM
ORM思想的概述
  • 建立两个映射关系:
  1. 实体类和表的映射关系
  2. 实体类中属性和表中字段的映射关系
  • 不再重点关注:sql语句
  • 主要目的:操作实体类就相当于操作数据库表
  • orm思想: 一个框架,一个想法
  • orm框架: 实现了这种思想的框架
  1. Mybatis
  2. Hibernate
jpa介绍

在这里插入图片描述

JPA接口也一样,后续我们更换框架也不需要变更代码,只需要更换底层配置即可 我们用Java定义的一套接口开发.当我们框架使用变更的时候就不用变更底层代码了

  1. JPA 是一套规范,它不干活,干活的是实现了JPA这一套接口的框架
  2. 我们掌握了JPA 的使用就相当于掌握了所有实现JPA的框架的使用
优点:
  • 简单方便
  • 标准

在这里插入图片描述

入门案例

创建步骤
  1. 导入jar (Manven 到如配置文件即可) 2) 编写JPA 的核心配置文件 3) 编写客户的实体类 4) 配置映射关系
  2. 保存客户信息到数据库中
  • 配置文件 META-INF/persistence.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
          <!--  配置持久化单元:
              name:持久化单元名称
              transaction-type:事物类型
                   RESOURCE_LOCAL:本地事务管理
                   JTA:分布式事务管理
           -->
      <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
      <!-- 配置jpa规范的服务提供商 -->
      <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
          <!-- 数据库配置
              用户名:javax.persistence.jdbc.user
              密码:javax.persistence.jdbc.password
              url:javax.persistence.jdbc.url
              数据库驱动:javax.persistence.jdbc.driver
           -->
      <properties>
      
          <!-- 数据库路径 -->
          <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpa"/>
          <!-- 数据库用户名 -->
          <property name="javax.persistence.jdbc.user" value="root"/>
          <!-- 数据库密码 -->
          <property name="javax.persistence.jdbc.password" value="123"/>
          <!-- 数据库驱动 -->
          <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
          <!--配置jpa实现方(hibernate)的配置信息
                         显示sql           :   false|true
                         自动创建数据库表    :  hibernate.hbm2ddl.auto
                                 create      : 程序运行时创建数据库表(如果有表,先删除表再创建)
                                 update      :程序运行时创建表(如果有表,不会创建表)
                                 none        :不会创建表
      
                     -->
          <property name="hibernate.show_sql" value="true" />
          <property name="hibernate.format_sql" value="true" />
          <property name="hibernate.hbm2ddl.auto" value="update" />
      
      
      
      </properties>
      </persistence-unit>
      </persistence>
    
  • 实体类

 package cn.itcast.domain;
 
 import javax.persistence.*;
 
 /**  客户的实体类      
   配置映射关系    
	   1.实体类和表的映射关系    
   		 @Entity:声明实体类    
  		 @Table : 配置实体类和表的映射关系      
    			    name :  配置数据库表的名称  
 2.实体类中属性和表中字段的映射关系 
  */
 
@Entity
@Table(name = "cst_customer")
public class Customer {


/**
 * @Id:声明主键的配置
 * @GeneratedValue:配置主键的生成策略
 *      strategy
 *          GenerationType.IDENTITY :自增,mysql
 *                 * 底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增)
 *          GenerationType.SEQUENCE : 序列,oracle
 *                  * 底层数据库必须支持序列
 *          GenerationType.TABLE : jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
 *          GenerationType.AUTO : 由程序自动的帮助我们选择主键生成策略
 * @Column:配置属性和字段的映射关系
 *      name:数据库表中字段的名称
 */
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@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;
}

@Override
public String toString() {
    return "Customer{" +
            "custId=" + custId +
            ", custName='" + custName + '\'' +
            ", custSource='" + custSource + '\'' +
            ", custLevel='" + custLevel + '\'' +
            ", custIndustry='" + custIndustry + '\'' +
            ", custPhone='" + custPhone + '\'' +
            ", custAddress='" + custAddress + '\'' +
            '}';
}}
  • 测试
package cn.itcast.test;

import cn.itcast.domain.Customer; import cn.itcast.utils.JpaUtils;
import org.junit.jupiter.api.Test;

import javax.persistence.EntityManager; import
javax.persistence.EntityTransaction;

public class JpaTest {
    /**
     *  测试jpa的保存
     *      案例:保存一个用户到数据库中
     *  Jpa的操作步骤:
     *      1,加载配置文件创建工厂(实体类管理器工厂)对象
     *      2,通过实体类管理器工厂获取实体类管理器
     *      3,获取事务对象,开启事务
     *      4,完成增删改查操作
     *      5,提交或回滚事物
     *      6,释放资源
     */
   @Test
   public void testSave(){
     //1,加载配置文件创建工厂(实体类管理器工厂)对象
     //EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
     // 2,通过实体类管理器工厂获取实体类管理器
     //  EntityManager em = factory.createEntityManager();
         EntityManager em= JpaUtils.getEntityManager();
        // 3,获取事务对象,开启事务
     EntityTransaction tx = em.getTransaction();//获取事务
        tx.begin();// 开启事务
    // 4,完成增删改查操作
    Customer customer=new Customer();
    customer.setCustName("杜金龙");
        customer.setCustPhone("13598322004");
         customer.setCustAddress("河南郑州");
         em.persist(customer);
        // 5,提交或回滚事物
         tx.commit();
        // 6,释放资源
        em.close();
     }


@Test
public void testFind(){
    // 1,通过工具类获取EntityManager对象
    EntityManager em= JpaUtils.getEntityManager();
    // 3,获取事务对象,开启事务
    EntityTransaction tx = em.getTransaction();//获取事务
    tx.begin();// 开启事务
    // 4,完成增删改查操作
    /**
     * find:根据id查询数据
     *      class:要查询数据的结果要包装的实体类类型的字节码
     *      id:查询的主键的取值
     */
    Customer customer = em.find(Customer.class, 1l);
    System.out.println(customer);

    // 5,提交或回滚事物
    tx.commit();
    // 6,释放资源
    em.close();
}

/**
 * 根据id查询客户
 *      getReference方法
 *          1.获取的对象是一个动态代理对象
 *          2.调用getReference方法不会立即发送sql语句查询数据库
 *              * 当调用查询结果对象的时候,才会发送查询的sql语句:什么时候用,什么时候发送sql语句查询数据库
 *
 * 延迟加载(懒加载)
 *      * 得到的是一个动态代理对象
 *      * 什么时候用,什么使用才会查询
 */
@Test
public void testReference(){
    // 1,通过工具类获取EntityManager对象
    EntityManager em= JpaUtils.getEntityManager();
    // 3,获取事务对象,开启事务
    EntityTransaction tx = em.getTransaction();//获取事务
    tx.begin();// 开启事务
    // 4,完成增删改查操作
    /**
     * getReference:根据id查询数据
     *      class:要查询数据的结果要包装的实体类类型的字节码
     *      id:查询的主键的取值
     */
    Customer customer = em.getReference(Customer.class, 2l);
    System.out.println(customer);

    // 5,提交或回滚事物
    tx.commit();
    // 6,释放资源
    em.close();
}

/**
 * remove  删除操作
 */
@Test
public void testRemove(){
    // 1,通过工具类获取EntityManager对象
    EntityManager em= JpaUtils.getEntityManager();
    // 3,获取事务对象,开启事务
    EntityTransaction tx = em.getTransaction();//获取事务
    tx.begin();// 开启事务
    // 4,完成增删改查操作
    //i 根据id查询客户
    Customer customer= em.find(Customer.class, 1l);
    //ii 调用remove方法进行删除操作
    em.remove(customer);
    // 5,提交或回滚事物
    tx.commit();
    // 6,释放资源
    em.close();
}

/**
 * merge  更新操作
 */
@Test
public void testMerge(){
    // 1,通过工具类获取EntityManager对象
    EntityManager em= JpaUtils.getEntityManager();
    // 3,获取事务对象,开启事务
    EntityTransaction tx = em.getTransaction();//获取事务
    tx.begin();// 开启事务
    // 4,完成增删改查操作
    //i 根据id查询客户
    Customer customer= em.find(Customer.class, 2l);
    customer.setCustName("李二狗");
    //ii 调用merge方法进行更新操作
    em.merge(customer);
    // 5,提交或回滚事物
    tx.commit();
    // 6,释放资源
    em.close();
} }
API讲解
加载配置文件创建实体管理器工厂
			Persisitence:静态方法(根据持久化单元名称创建实体管理器工厂)
				createEntityMnagerFactory(持久化单元名称)
			作用:创建实体管理器工厂
			
	2.根据实体管理器工厂,创建实体管理器
		EntityManagerFactory :获取EntityManager对象
		方法:createEntityManager
		* 内部维护的很多的内容
			内部维护了数据库信息,
			维护了缓存信息
			维护了所有的实体管理器对象
			再创建EntityManagerFactory的过程中会根据配置创建数据库表
		* EntityManagerFactory的创建过程比较浪费资源
		特点:线程安全的对象
			多个线程访问同一个EntityManagerFactory不会有线程安全问题
		* 如何解决EntityManagerFactory的创建过程浪费资源(耗时)的问题?
		思路:创建一个公共的EntityManagerFactory的对象
		* 静态代码块的形式创建EntityManagerFactory
		
	3.创建事务对象,开启事务
		EntityManager对象:实体类管理器
			getTransaction : 创建事务对象
			presist : 保存
			merge  : 更新
			remove : 删除
			find/getRefrence : 根据id查询
			
		Transaction 对象 : 事务
			begin:开启事务
			commit:提交事务
			rollback:回滚
	4.增删改查操作
	5.提交事务
	6.释放资源
抽取jpaUtils工具类
package cn.itcast.utils;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
 * 解决时期管理器工厂浪费资源和耗时问题
 *      通过静态代码块的形式,当程序第一次访问此工具类的时候,创建一个共有的实体管理器工厂对象
 * 第一次访问getEntityManager方法:经过静态代码块创建一个factory对象,在调用方法创建一个entityManager对象.
 * 第二次访问的时候,fatory已经存在,直接通过已经创建好的fatory对象创建一个entityManager对象.
 */
public class JpaUtils {
    private static EntityManagerFactory factory;
    static{
        //加载配置文件,创建一个entityManagerFactory工厂对象
        factory= Persistence.createEntityManagerFactory("myJpa");
    }

    public static EntityManager getEntityManager(){
        //通过过工厂对象获取实体管理器对象
        return factory.createEntityManager();
    }
}
jpql的介绍
package cn.itcast.test;

import cn.itcast.utils.JpaUtils;
import org.junit.jupiter.api.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import java.util.List;

/**
 * 测试jqpl查询
 */
public class JpqlTest {
    /**
     * 查询全部
     */
    @Test
    public void testFindAll(){
        // 1,通过工具类获取EntityManager对象
        EntityManager em= JpaUtils.getEntityManager();
        // 3,获取事务对象,开启事务
        EntityTransaction tx = em.getTransaction();//获取事务
        tx.begin();// 开启事务
        // 4,完成增删改查操作
        /**
         * 查询全部
         *      jqpl:from cn.itcast.domain.Customer
         *      sql:SELECT * FROM cst_customer
         */
        //创建jpql语句
        String jpql="from Customer ";
        //获取query查询对象
        Query query = em.createQuery(jpql);
        //发送查询 并封装结果集
        List list = query.getResultList();
        for(Object obj:list){
            System.out.println(obj);
        }

        // 5,提交或回滚事物
        tx.commit();
        // 6,释放资源
        em.close();
    }

    /**
     * 排序查询: 倒序查询全部客户(根据id倒序)
     *      sql:SELECT * FROM cst_customer ORDER BY cust_id DESC
     *      jpql:from Customer order by custId desc
     *
     * 进行jpql查询
     *      1.创建query查询对象
     *      2.对参数进行赋值
     *      3.查询,并得到返回结果
     */
    @Test
    public void testOrders(){
        // 1,通过工具类获取EntityManager对象
        EntityManager em= JpaUtils.getEntityManager();
        // 3,获取事务对象,开启事务
        EntityTransaction tx = em.getTransaction();//获取事务
        tx.begin();// 开启事务
        // 4,完成增删改查操作
        //创建query查询对象
        String jpql="from Customer  order by custId desc";
        Query query = em.createQuery(jpql);
        //对参数进行赋值
         //查询,并得到返回结果
        List list = query.getResultList();
        for(Object obj:list){
            System.out.println(obj);
        }

        // 5,提交或回滚事物
        tx.commit();
        // 6,释放资源
        em.close();
    }

    /**
     * 使用jpql查询,统计客户的总数
     *      sql:SELECT COUNT(cust_id) FROM cst_customer
     *      jpql:select count(custId) from Customer
     */
    @Test
    public void testCount(){
        // 1,通过工具类获取EntityManager对象
        EntityManager em= JpaUtils.getEntityManager();
        // 3,获取事务对象,开启事务
        EntityTransaction tx = em.getTransaction();//获取事务
        tx.begin();// 开启事务
        // 4,完成增删改查操作
        //创建query查询对象
        String jpql="select count(custId) from Customer";
        Query query = em.createQuery(jpql);
        //对参数进行赋值
        //查询,并得到返回结果
        /**
         * getResultList : 直接将查询结果封装为list集合
         * getSingleResult : 得到唯一的结果集
         */
        Object o = query.getSingleResult();
        System.out.println(o);

        // 5,提交或回滚事物
        tx.commit();
        // 6,释放资源
        em.close();
    }

    /**
     * 分页查询
     *      sql:select * from cst_customer limit 0,2
     *      jqpl : from Customer
     */

    @Test
    public void testPage(){
        // 1,通过工具类获取EntityManager对象
        EntityManager em= JpaUtils.getEntityManager();
        // 3,获取事务对象,开启事务
        EntityTransaction tx = em.getTransaction();//获取事务
        tx.begin();// 开启事务
        // 4,完成增删改查操作
        //创建query查询对象
        String jpql="from Customer";
        Query query = em.createQuery(jpql);
        //对参数进行赋值 --分页参数
        //1,起始索引
        query.setFirstResult(0);
        //2,每页查询条数
        query.setMaxResults(2);
        //查询,并得到返回结果
        /**
         * getResultList : 直接将查询结果封装为list集合
         * getSingleResult : 得到唯一的结果集
         */

        List list = query.getResultList();
        for(Object o:list){
            System.out.println(o);
        }
        // 5,提交或回滚事物
        tx.commit();
        // 6,释放资源
        em.close();
    }

    /**
     * 条件查询
     *     案例:查询客户名称以‘杜’开头的客户
     *          sql:SELECT * FROM cst_customer WHERE cust_name LIKE  ?
     *          jpql : from Customer where custName like ?
     */
    @Test
    public void testCondition(){
        // 1,通过工具类获取EntityManager对象
        EntityManager em= JpaUtils.getEntityManager();
        // 3,获取事务对象,开启事务
        EntityTransaction tx = em.getTransaction();//获取事务
        tx.begin();// 开启事务
        // 4,完成增删改查操作
        //创建query查询对象
        String jpql="from Customer where custName like ?";
        Query query = em.createQuery(jpql);
        //对参数进行赋值
        query.setParameter(1,"杜%");
        //查询,并得到返回结果
        /**
         * getResultList : 直接将查询结果封装为list集合
         * getSingleResult : 得到唯一的结果集
         */

        List list = query.getResultList();
        for(Object o:list){
            System.out.println(o);
        }
        // 5,提交或回滚事物
        tx.commit();
        // 6,释放资源
        em.close();
    }
}

SpringDataJpa

Spring-data-jpa的基本介绍:JPA诞生的缘由是为了整合第三方ORM框架,建立一种标准的方式,百度百科说是JDK为了实现ORM的天下归一,目前也是在按照这个方向发展,但是还没能完全实现。在ORM框架中,Hibernate是一支很大的部队,使用很广泛,也很方便,能力也很强,同时Hibernate也是和JPA整合的比较良好,我们可以认为JPA是标准,事实上也是,JPA几乎都是接口,实现都是Hibernate在做,宏观上面看,在JPA的统一之下Hibernate很良好的运行。
在这里插入图片描述
在这里插入图片描述

入门案例
  • pom.xml maven坐标导入
    spring核心
    spring AOP
    spring IOC
    spring ORM
    springDataJPA
    hibernate
    mysql驱动
    log4j日志
配置文件 META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!-- 配置的扫描的包(实体类所在的包) -->
    <property name="packagesToScan" value="cn.itcast.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="123"></property>
    <property name="jdbcUrl" value="jdbc:mysql:///jpa"></property>
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
</bean>

<!-- 3, 整合spring data jpa -->
<jpa:repositories base-package="cn.itcast.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"></property>
 </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(* cn.itcast.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>

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

<!-- 6,配置包扫描 -->
<context:component-scan base-package="cn.itcast"></context:component-scan>
![在这里插入图片描述](https://img-blog.csdn.net/20180923140822382?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM5ODc2NjY2/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) ![在这里插入图片描述](https://img-blog.csdn.net/20180923140911197?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM5ODc2NjY2/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
  1. SpringdataJPA 底层是创建了Dao 代理对象,
  2. SpringdataJPA 底层依旧是封装了EntityManager 对象进行数据库操作
内部执行过程
  1. 通过JdkDynamicAopProxy的invoke方法创建了一个动态代理对象
  2. SimpleJpaRepository当中封装了JPA的操作(借助JPA的api完成数据库的CRUD)
  3. 通过hibernate完成数据库操作(封装了jdbc)

在这里插入图片描述

jpql的查询方式
		jpql : jpa query language  (jpq查询语言)
		特点:语法或关键字和sql语句类似
			  查询的是类和类中的属性
			
	需要将JPQL语句配置到接口方法上
		1.特有的查询:需要在dao接口上配置方法
		2.在新添加的方法上,使用注解的形式配置jpql查询语句
		3.注解 : @Query()
在测试的过程中,@Transactional,@Rollback(value = false)的作用,update save delete
/**
 * 测试jpql的更新操作
 *      * springDataJpa中使用jpql完成 更新和删除操作
 *                  * 需要手动的添加事物的支持
 *                  * 默认执行结束后,回滚事务
 *      @Rollback : 设置是否自动回滚
 *                  false | true
 */
@Test
@Transactional
@Rollback(value = false)
public void testUpdateById(){
    customerDao.updateById(4l,"王八");
}
占位符问题
/**
 * 案例:根据客户名称和客户id查询客户
 *      jpql: from Customer where custName = ? and custId = ?
 *
 *  对于多个占位符参数
 *      赋值的时候,默认的情况下,占位符的位置需要和方法参数中的位置保持一致
 *
 *  可以指定占位符参数的位置
 *      ? 索引的方式,指定此占位的取值来源
 */
 @Query(value = "from Customer where custName = ?2 and custId = ?1")
 public Customer findCustNameAndId(Long id, String name);
dao层接口,@Modifying

@Query : 代表的是进行查询
* 声明此方法是用来进行更新操作
@Modifying
* 当前执行的是一个更新操作,

getOne() 方法
	因为是延迟加载, 如果没有事务等我们使用对象的时候,数据可能已经发生了变化,为了保证数据的一致性,
	我们需要一个事务
 /**
     * 根据id从数据库查询
     *           @Transactional:保证getOne正常运行
     *findone:
     *      em.find()          :立即加载
     * getOne():
     *        em.getReference   :延迟加载\
     *          *返回的是一个客户端的动态代理对象
     *          *什么时候用  什么时候查询
     */
    @Test
    @Transactional
    public void testGetOne(){
        Customer one = customerDao.getOne(4l);
        System.out.println(one);
    }
sql查询
/**
 * 使用sql的形式查询:
 *     查询全部的客户
 *  sql : select * from cst_customer;
 *  Query : 配置sql查询
 *      value : sql语句
 *      nativeQuery : 查询方式
 *          true : sql查询
 *          false:jpql查询
 *
 */
 @Query(value="select * from cst_customer ",nativeQuery = true)
 public List<Object [] > findSql(String name);
 上面的时候去一个Object[] ,如果要获取一个对象的话,我们不能用* ,要写上全部的字段名称
 @Query(value="select cust_name,cust_id,cust_address,cust_industry,cust_level,cust_phone,cust_source 
 from cst_customer where cust_name like ?1",nativeQuery = true)

public List findSql(String name);

方法命名规则查询

方法命名规则查询 是对JPQL的进一步封装

/**
 * 方法名的约定:
 *      findBy : 查询
 *            对象中的属性名(首字母大写) : 查询的条件
 *            CustName
 *            * 默认情况 : 使用 等于的方式查询
 *                  特殊的查询方式
 *
 *  findByCustName   --   根据客户名称查询
 *
 *  再springdataJpa的运行阶段
 *          会根据方法名称进行解析  findBy    from  xxx(实体类)
 *                                      属性名称      where  custName =
 *
 *      1.findBy  + 属性名称 (根据属性名称进行完成匹配的查询=)
 *      2.findBy  + 属性名称 + “查询方式(Like | isnull)”
 *          findByCustNameLike
 *      3.多条件查询
 *          findBy + 属性名 + “查询方式”   + “多条件的连接符(and|or)”  + 属性名 + “查询方式”
 */

Specifications动态查询

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

/**
         * 自定义查询条件
         *      1,实现Specification接口(提供泛型: 查询的对象类型)
         *      2,实现toPredicate方法(构造查询条件)
         *      3,需要借助方法参数中的两个参数(
         *            root: 获取需要查询对象的属性
         *            CriteriaBuilder: 构造查询条件的,内部封装了很多查询条件(模糊匹配,精准匹配)
         *      )
		 * 案例:完成根据客户名称的模糊匹配,返回客户列表
		 *      客户名称以'杜'开头
		 * equal: 直接得到path对象(属性),然后进行比较即可
		  * gt,lt,le,like : 得到path对象,根据path指定的比较的参数类型,再去进行比较
		 *          指定的参数类型: path.as(类的字节码对象)---custName.as(String.class)
		 */
@Test
public void testSpec2(){
    Specification<Customer> spec=new Specification<Customer>() {
        public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            //查询属性名
            Path<Object> custName = root.get("custName");
            //查询方式: 模糊匹配
            Predicate like = cb.like(custName.as(String.class), "杜%");
            return like;
        }
    };
    /**
     * 添加排序
     * 创建排序对象,需要调用构造方法实例化sort对象
     * 第一个参数: 排序的顺序: (倒序,升序)
     *          Sort.Direction.DESC: 倒序
     *          Sort.Direction.ASC: 升序
     * 第二个参数: 排序的属性名称
     */
    Sort sort=new Sort(Sort.Direction.DESC,"custId");
    List<Customer> list = customerDao.findAll(spec, sort);
    for (Customer customer : list) {
        System.out.println(customer);
    }
}
方法对应关系

在这里插入图片描述

多表设计
  • 表关系
    在这里插入图片描述
  • 在JPA框架中表关系的分析步骤
  1. 首先确定两张表之间的关系。

  2. 在数据库中实现两张表的关系

  3. 在实体类中描述出两个实体的关系

  4. 配置出实体类和数据库表的关系映射(重点)

JPA中的一对多

在这里插入图片描述

@OneToMany:
   	作用:建立一对多的关系映射
    属性:
    	targetEntityClass:指定多的多方的类的字节码
    	mappedBy:指定从表实体类中引用主表对象的名称。
    	cascade:指定要使用的级联操作
    	fetch:指定是否采用延迟加载
    	orphanRemoval:是否使用孤儿删除

@ManyToOne
    作用:建立多对一的关系
    属性:
    	targetEntityClass:指定一的一方实体类字节码
    	cascade:指定要使用的级联操作
    	fetch:指定是否采用延迟加载
    	optional:关联是否可选。如果设置为false,则必须始终存在非空关系。

@JoinColumn
     作用:用于定义主键字段和外键字段的对应关系。
     属性:
    	name:指定外键字段的名称
    	referencedColumnName:指定引用主表的主键字段名称
    	unique:是否唯一。默认值不唯一
    	nullable:是否允许为空。默认值允许。
    	insertable:是否允许插入。默认值允许。
    	updatable:是否允许更新。默认值允许。
    	columnDefinition:列的定义信息。
    	 /**
	主表的配置注解:
	    * 放弃外键的维护权
	    *      mappedBy:对方配置关系的属性名称
 		* cascade : 配置级联(可以配置到设置多表的映射关系的注解上)
 		*             cascadeType.all      :所有
		*                         merge    :更新
	    *                         persist  :保存
 		*                         remove   :删除
	    *     fetch:配置关联对象的加载方式
 		*              EAGER   :  立即加载
	    *              LAZY    :  延迟加载
		*/
		@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
	    private Set<LinkMan> linkMans=new HashSet<LinkMan>();
    从表的注解配置:
	     /**
		 * 配置联系人到客户的多对一关系
 		 *     使用注解的形式配置多对一关系
 		 *      1.配置表关系
 		 *          @ManyToOne : 配置多对一关系
		 *              targetEntity:对方的实体类字节码
 	     *      2.配置外键(中间表)
 		 *
 		 * 配置外键的过程,配置到了多的一方,就会在多的一方维护外键
		 */
@ManyToOne(targetEntity = Customer.class,fetch = FetchType.LAZY)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Customer customer;
  • 删除操作的说明如下

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

删除主表数据:

 有从表数据

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

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

在实际开发中,级联删除请慎用!(在一对多的情况下)

  • 级联操作

级联操作:指操作一个对象同时操作它的关联对象

使用方法:只需要在操作主体的注解上配置cascade

第4章 JPA中的多对多
  • 表关系确立
    在这里插入图片描述
@ManyToMany 	
	作用:用于映射多对多关系 	
	属性:
	    cascade:配置级联操作。 		
		fetch:配置是否采用延迟加载。
    	targetEntity:配置目标的实体类。映射多对多的时候不用写。

@JoinTable
    作用:针对中间表的配置
    属性:
    	nam:配置中间表的名称
    	joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段			  			
    	inverseJoinColumn:中间表的外键字段关联对方表的主键字段
    	 @JoinColumn
    作用:用于定义主键字段和外键字段的对应关系。
    属性:
    	name:指定外键字段的名称
    	referencedColumnName:指定引用主表的主键字段名称
    	unique:是否唯一。默认值不唯一
    	nullable:是否允许为空。默认值允许。
    	insertable:是否允许插入。默认值允许。
    	updatable:是否允许更新。默认值允许。
    	columnDefinition:列的定义信息。
  • 从表关联属性注解

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

@ManyToMany(mappedBy = "roles")
private Set<User> users=new HashSet<User>();
  • 主表关联属性注解
/**
     * 配置用户到角色的多对多关系
     *      配置多对多的映射关系
     *          1,声明关系的设置
     *              @ManyToMany(targetEntity = Role.class) //多对多
     *                  targetEntity: 代表对方实体类的字节码
     *          2,配置中间表(包含两个外键)
     *              @JoinTable
     *                  name: 中间表的名称
     *                  joinColumns:当前对象在中间表中的外键
     *                      @JoinColumn的数组
     *                          name: 外键名
     *                          referencedColumnName: 参照得主表的主键名
     *                  nverseJoinColumn:对方对象在中间表的外键
     */
    @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
    @JoinTable(name = "sys_user_role",
            //joinColumns: 当前对象在中间表中的外键
            joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
            //inverseJoinColumns: 对方对象在中间表的外键
            inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")}
    )
    private Set<Role> roles=new HashSet<Role>();
Spring Data JPA中的多表查询
  • 对象导航查询
  1. 我们查询客户时,要不要把联系人查询出来?

分析:如果我们不查的话,在用的时候还要自己写代码,调用方法去查询。如果我们查出来的,不使用时又会白白的浪费了服务器内存。

解决:采用延迟加载的思想。通过配置的方式来设定当我们在需要使用时,发起真正的查询。

配置方式:

/**
 * 在客户对象的@OneToMany注解中添加fetch属性
 * 		FetchType.EAGER	:立即加载
 * 		FetchType.LAZY	:延迟加载
 */
@OneToMany(mappedBy="customer",fetch=FetchType.EAGER)
private Set<LinkMan> linkMans = new HashSet<>(0);
  1. 我们查询联系人时,要不要把客户查询出来?

分析:例如:查询联系人详情时,肯定会看看该联系人的所属客户。如果我们不查的话,在用的时候还要自己写代码,调用方法去查询。如果我们查出来的话,一个对象不会消耗太多的内存。而且多数情况下我们都是要使用的。

解决: 采用立即加载的思想。通过配置的方式来设定,只要查询从表实体,就把主表实体对象同时查出来
配置方式

/**
	 * 在联系人对象的@ManyToOne注解中添加fetch属性
	 * 		FetchType.EAGER	:立即加载
	 * 		FetchType.LAZY	:延迟加载
	 */
	@ManyToOne(targetEntity=Customer.class,fetch=FetchType.EAGER)
	@JoinColumn(name="cst_lkm_id",referencedColumnName="cust_id")
	private Customer customer;
  • 使用Specification查询
/**
	 * Specification的多表查询
	 */
	@Test
	public void testFind() {
		Specification<LinkMan> spec = new Specification<LinkMan>() {
			public Predicate toPredicate(Root<LinkMan> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				//Join代表链接查询,通过root对象获取
				//创建的过程中,第一个参数为关联对象的属性名称,第二个参数为连接查询的方式(left,inner,right)
				//JoinType.LEFT : 左外连接,JoinType.INNER:内连接,JoinType.RIGHT:右外连接
				Join<LinkMan, Customer> join = root.join("customer",JoinType.INNER);
				return cb.like(join.get("custName").as(String.class),"传智播客1");
			}
		};
		List<LinkMan> list = linkManDao.findAll(spec);
		for (LinkMan linkMan : list) {
			System.out.println(linkMan);
		}
	}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值