SpringDataJPA入门及四种查询方式

SpringDataJPA 与 JPA 和 Hibernate 之间的关系

在说它们之间的关系之前,先说一说 JPA 和 Hibernate。JPA 是 SUN 公司推出的一套基于 ORM 的规范,内部由一系列的接口和抽象类构成。而 Hibernate 是实现了 JPA 规范的框架,它封装了 JDBC,可以对数据库进行操作。SpringDataJPA 是 对 JPA 规范的进一步封装,它并没有实现 JPA 的操作,而真正对数据的增删改查还是要依赖 Hibernate 等框架。下面这一张图诠释了它们之间的关系:
在这里插入图片描述
我们在代码中使用的是 SpringDataJPA 提供给我们的方法,SpringDataJPA 封装了 JPA,它的方法中还需要调用 JPA 规范中定义的方法,而 JPA 是一些接口和抽象类,所以最终还是需要依赖 Hibernate 等实现了 JPA 规范的框架来对数据库进行操作。

SpringDataJPA快速入门(与Spring的整合)

1)首先需要导入坐标
要整合 Spring 和 SpringDataJPA,并且还需要提供 JPA 的服务提供者 HIbernate,所以需要导入 Spring、SpringDataJPA 相关坐标,Hibernate 相关坐标和数据库驱动坐标等。

<properties>
    <spring.version>4.2.4.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>5.1.6</mysql.version>
  </properties>

  <dependencies>
    <!-- junit单元测试 -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.9</version>
      <scope>test</scope>
    </dependency>

    <!-- spring beg -->
    <!-- spring aop 相关 -->
    <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>
    <!-- spring aop 相关 end -->

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

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

2)SpringDataJPA 与 Spring 整合
Spring 整合其他框架最繁琐的就是配置文件,所以接下来要在配置文件中写一些关于 SpringDataJPA 的配置。

<!-- 1.创建entityManagerFactory对象交给spring容器管理 -->
  <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"></bean>
    </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"></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:///jpa"/>
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
  </bean>

  <!-- 3.整合spring dataJpa -->
  <!--dao接口所在的包-->
  <jpa:repositories base-package="cn.itcast.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="cn.itcast" />

3)用注解建立实体和数据库表的映射关系
此步骤省略
4)编写符合 SpringDataJPA 规范的 Dao 层接口
符合 SpringDataJPA 规范,就是需要接口 实现 JpaRepository 和 JpaSpecificationExecutor 接口

public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer>

5)完成以上步骤之后,就可以使用定义好的 Dao 层接口完成基本的 CRUD 操作

@Test
public void testFindOne(){
    Customer customer = customerDao.findOne(3L);
    System.out.println(customer);
}
SpringDataJPA的内部原理分析

1)疑问
在使用 SpringDataJPA 时,一般实现 JpaRepository 和 JpaSpecificationExecutor 接口,这样就可以使用这些接口中定义的方法,但是这些方法都只是一些声明,没有具体的实现方式,那么在 SpringDataJPA 中它又是怎么实现的呢?

2)实现过程
我们以debug断点调试的方式,通过分析 SpringDataJPA 的源码来分析程序的执行过程,以上述的findOne方法为例进行分析

  • 代理子类的实现过程
    在这里插入图片描述
    断点执行到方法上时,我们可以发现注入的 customerDao 对象,本质上是通过 JdkDynamicAopProxy 生成的一个代理对象
  • 代理对象中方法调用的分析
    当程序执行的时候,会通过 JdkDynamicAopProxy 的 invoke 方法,对 customerDao 对象生成动态代理对象。根据对 SpringDataJPA 介绍而知,要想进行findOne查询方法,最终还是会出现JPA规范的API完成操作,那么这些底层代码存在于何处呢?答案很简单,都隐藏在通过 JdkDynamicAopProxy 生成的动态代理对象当中,而这个动态代理对象就是 SimpleJpaRepository
    在这里插入图片描述
    通过SimpleJpaRepository的源码分析,定位到了findOne方法,在此方法中,返回em.find()的返回结果,那么em又是什么呢?
    在这里插入图片描述
    带着问题继续查找 em 对象,我们发现 em 就是 EntityManager 对象,而他是 JPA 原生的实现方式,所以我们得到结论 SpringDataJPA 只是对标准 JPA 操作进行了进一步封装,简化了 Dao 层代码的开发

3)总结
SpringDataJPA 的执行过程是基于 JDK 的动态代理,1.通过 JdkDynamicAopProxy 的 invoke 方法创建了一个动态代理对象 SimpleJpaRepository;2.SimpleJpaRepository 当中封装了JPA的操作(借助JPA的api完成数据库的CRUD);3.通过 hibernate 完成数据库操作(封装了jdbc)

SpringDataJPA 的四种查询方式

1)使用 SpringDataJPA 中接口定义的方法进行查询
在继承了 JpaRepository 和 JpaRepository 接口后,我们就可以使用接口中定义的方法进行查询

  • save(customer):保存或者更新(依据传递的实体类对象中,是否包含id属性。如果不包含则保存;如果包含则更新【实际上更新之前先查询,只有查询到有数据,才会更新】)
  • delete(id):根据id删除
  • findAll():查询全部
  • findOne(id):根据id查询(调用的是 entityManager 的 find() 方法,是立即加载)
  • getOne(id):根据id查询(调用的是 entityManager 的 getRefrence() 方法,是延迟加载)

2)使用JPQL的方式查询
使用 SpringDataJPA 提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用 @Query 注解,结合 JPQL 的语句方式完成查询
@Query 注解的使用非常简单,只需在方法上面标注该注解,同时提供一个JPQL查询语句即可

public interface CustomerDao extends JpaRepository<Customer, Long>,JpaSpecificationExecutor<Customer> {    
    //@Query 使用jpql的方式查询。
    @Query(value="from Customer")
    public List<Customer> findAllCustomer();
    
    //@Query 使用jpql的方式查询。?1代表参数的占位符,其中1对应方法中的参数索引
    @Query(value="from Customer where custName = ?1")
    public Customer findCustomer(String custName);
}

此外,也可以通过使用 @Query 来执行一个更新操作,为此,我们需要在使用 @Query 的同时,用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询

@Query(value="update Customer set custName = ?1 where custId = ?2")
@Modifying
public void updateCustomer(String custName,Long custId);

3)使用 SQL 语句查询
Spring Data JPA同样也支持sql语句的查询,如下:

/**
 * nativeQuery : 使用本地sql的方式查询
 */
@Query(value="select * from cst_customer",nativeQuery=true)
public void findSql();

4)使用 SpringDataJPA 提供的方法命名规则查询
顾名思义,方法命名规则查询就是根据方法的名字,就能创建查询。只需要按照Spring Data JPA提供的方法命名规则定义方法的名称,就可以完成查询工作。Spring Data JPA在程序执行的时候会根据方法名称进行解析,并自动生成查询语句进行查询。

按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。

//方法命名方式查询,不需要加 @Query 注解(根据客户名称查询客户)
public Customer findByCustName(String custName);

具体的关键字,使用方法和生产成SQL如下表所示
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值