欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

spring data jpa(一)

程序员文章站 2022-04-04 14:54:26
第1章 Spring Data JPA的快速入门 1.1 需求说明 Spring Data JPA完成客户的基本CRUD操作 1.2 搭建Spring Data JPA的开发环境 1.2.1 引入Spring Data JPA的坐标 使用Spring Data JPA,需要整合Spring与Spri ......

第1章     spring data jpa的快速入门

1.1   需求说明

spring data jpa完成客户的基本crud操作

1.2   搭建spring data jpa的开发环境

1.2.1      引入spring data jpa的坐标

使用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.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 -->

        <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>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>4.2.4.release</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>

 

 

1.2.2      整合spring data jpa与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">

   

    <!-- 1.datasource 配置数据库连接池-->

    <bean id="datasource" class="com.mchange.v2.c3p0.combopooleddatasource">

        <property name="driverclass" value="com.mysql.jdbc.driver" />

        <property name="jdbcurl" value="jdbc:mysql://localhost:3306/jpa" />

        <property name="user" value="root" />

        <property name="password" value="111111" />

    </bean>

   

    <!-- 2.配置entitymanagerfactory -->

    <bean id="entitymanagerfactory" class="org.springframework.orm.jpa.localcontainerentitymanagerfactorybean">

        <property name="datasource" ref="datasource" />

        <property name="packagestoscan" value="cn.itcast.entity" />

        <property name="persistenceprovider">

            <bean class="org.hibernate.jpa.hibernatepersistenceprovider" />

        </property>

        <!--jpa的供应商适配器-->

        <property name="jpavendoradapter">

            <bean class="org.springframework.orm.jpa.vendor.hibernatejpavendoradapter">

                <property name="generateddl" value="false" />

                <property name="database" value="mysql" />

                <property name="databaseplatform" value="org.hibernate.dialect.mysqldialect" />

                <property name="showsql" value="true" />

            </bean>

        </property>

        <property name="jpadialect">

            <bean class="org.springframework.orm.jpa.vendor.hibernatejpadialect" />

        </property>

    </bean>

   

   

    <!-- 3.事务管理器-->

    <!-- jpa事务管理器  -->

    <bean id="transactionmanager" class="org.springframework.orm.jpa.jpatransactionmanager">

        <property name="entitymanagerfactory" ref="entitymanagerfactory" />

    </bean>

   

    <!-- 整合spring data jpa-->

    <jpa:repositories base-package="cn.itcast.dao"

        transaction-manager-ref="transactionmanager"

        entity-manager-factory-ref="entitymanagerfactory"></jpa:repositories>

       

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

   

    <context:component-scan base-package="cn.itcast"></context:component-scan>

       

    <!--组装其它 配置文件-->

   

</beans>

 

 

1.2.3      使用jpa注解配置映射关系

我们使用昨天案例中的customer实体类对象,已经配置好了映射关系

 

package cn.itcast.entity;

 

import javax.persistence.column;

import javax.persistence.entity;

import javax.persistence.generatedvalue;

import javax.persistence.generationtype;

import javax.persistence.id;

import javax.persistence.table;

 

/**

 *

 *      * 所有的注解都是使用jpa的规范提供的注解,

 *      * 所以在导入注解包的时候,一定要导入javax.persistence下的

 */

@entity //声明实体类

@table(name="cst_customer") //建立实体类和表的映射关系

public class customer {

   

    @id//声明当前私有属性为主键

    @generatedvalue(strategy=generationtype.identity) //配置主键的生成策略

    @column(name="cust_id") //指定和表中cust_id字段的映射关系

    private long custid;

   

    @column(name="cust_name") //指定和表中cust_name字段的映射关系

    private string custname;

   

    @column(name="cust_source")//指定和表中cust_source字段的映射关系

    private string custsource;

   

    @column(name="cust_industry")//指定和表中cust_industry字段的映射关系

    private string custindustry;

   

    @column(name="cust_level")//指定和表中cust_level字段的映射关系

    private string custlevel;

   

    @column(name="cust_address")//指定和表中cust_address字段的映射关系

    private string custaddress;

   

    @column(name="cust_phone")//指定和表中cust_phone字段的映射关系

    private string custphone;

   

    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 getcustindustry() {

        return custindustry;

    }

    public void setcustindustry(string custindustry) {

        this.custindustry = custindustry;

    }

    public string getcustlevel() {

        return custlevel;

    }

    public void setcustlevel(string custlevel) {

        this.custlevel = custlevel;

    }

    public string getcustaddress() {

        return custaddress;

    }

    public void setcustaddress(string custaddress) {

        this.custaddress = custaddress;

    }

    public string getcustphone() {

        return custphone;

    }

    public void setcustphone(string custphone) {

        this.custphone = custphone;

    }

}

 

 

1.3   使用spring data jpa完成需求

1.3.1      编写符合spring data jpa规范的dao层接口

spring data jpa是spring提供的一款对于数据访问层(dao层)的框架,使用spring data jpa,只需要按照框架的规范提供dao接口,不需要实现类就可以完成数据库的增删改查、分页查询等方法的定义,极大的简化了我们的开发过程。

 

在spring data jpa中,对于定义符合规范的dao层接口,我们只需要遵循以下几点就可以了:

 

1.创建一个dao层接口,并实现jparepositoryjpaspecificationexecutor

    2.提供相应的泛型

 

package cn.itcast.dao;

 

import java.util.list;

 

import org.springframework.data.jpa.repository.jparepository;

import org.springframework.data.jpa.repository.jpaspecificationexecutor;

 

import cn.itcast.entity.customer;

 

/**

 * jparepository<实体类类型,主键类型>:用来完成基本crud操作

 * jpaspecificationexecutor<实体类类型>:用于复杂查询(分页等查询操作)

 */

public interface customerdao extends jparepository<customer, long>, jpaspecificationexecutor<customer> {

}

 

 

这样我们就定义好了一个符合spring data jpa规范的dao层接口

1.3.2      完成基本crud操作

完成了spring data jpa的环境搭建,并且编写了符合spring data jpa 规范的dao层接口之后,就可以使用定义好的dao层接口进行客户的基本crud操作

 

@runwith(springjunit4classrunner.class)

@contextconfiguration(locations="classpath:applicationcontext.xml")

public class customerdaotest {

 

    @autowired

    private customerdao customerdao;

   

    /**

     * 保存客户:调用save(obj)方法

     */

    @test

    public void testsave() {

        customer c = new customer();

        c.setcustname("传智播客");

        customerdao.save(c);

    }

   

    /**

     * 修改客户:调用save(obj)方法

     *      对于save方法的解释:如果执行此方法是对象中存在id属性,即为更新操作会先根据id查询,再更新   

     *                      如果执行此方法中对象中不存在id属性,即为保存操作

     *         

     */

    @test

    public void testupdate() {

        //根据id查询id为1的客户

        customer customer = customerdao.findone(1l);

        //修改客户名称

        customer.setcustname("传智播客顺义校区");

        //更新

        customerdao.save(customer);

    }

   

    /**

     * 根据id删除:调用delete(id)方法

     */

    @test

    public void testdelete() {

        customerdao.delete(1l);

    }

   

    /**

     * 根据id查询:调用findone(id)方法

     */

    @test

    public void testfindbyid() {

        customer customer = customerdao.findone(2l);

        system.out.println(customer);

    }

}

 

 

第2章     spring data jpa的内部原理剖析

2.1   spring data jpa的常用接口分析

在客户的案例中,我们发现在自定义的customerdao中,并没有提供任何方法就可以使用其中的很多方法,那么这些方法究竟是怎么来的呢?答案很简单,对于我们自定义的dao接口,由于继承了jparepository和jpaspecificationexecutor,所以我们可以使用这两个接口的所有方法。

 

 

在使用spring data jpa时,一般实现jparepository和jpaspecificationexecutor接口,这样就可以使用这些接口中定义的方法,但是这些方法都只是一些声明,没有具体的实现方式,那么在 spring data jpa中它又是怎么实现的呢?

2.2   spring data jpa的实现过程

通过对客户案例,以debug断点调试的方式,通过分析spring data jpa的原来来分析程序的执行过程

我们以findone方法为例进行分析

 

代理子类的实现过程

 通过jdkdynamicaopproxy 创建动态代理对象

动态代理对象:simplejparepository 实现了japrepository 和 jpaspecificationexecutor。对基本增删改查进行了封装。最终通过entitymanager完成增删改查操作spring data jpa(一)

 

 

 

断点执行到方法上时,我们可以发现注入的customerdao对象,本质上是通过jdkdynamicaopproxy生成的一个代理对象

 

 

代理对象中方法调用的分析

 

当程序执行的时候,会通过jdkdynamicaopproxy的invoke方法,对customerdao对象生成动态代理对象。根据对spring data jpa介绍而知,要想进行findone查询方法,最终还是会出现jpa规范的api完成操作,那么这些底层代码存在于何处呢?答案很简单,都隐藏在通过jdkdynamicaopproxy生成的动态代理对象当中,而这个动态代理对象就是simplejparepository

     

   

 

   

通过simplejparepository的源码分析,定位到了findone方法,在此方法中,返回em.find()的返回结果,那么em又是什么呢?

 

   

带着问题继续查找em对象,我们发现em就是entitymanager对象,而他是jpa原生的实现方式,所以我们得到结论spring data jpa只是对标准jpa操作进行了进一步封装,简化了dao层代码的开发

2.3   spring data jpa完整的调用过程分析

 

第3章     spring data jpa的查询方式

3.1   使用spring data jpa中接口定义的方法进行查询

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

 

继承jparepository后的方法列表

 delete

  删除

findall

  查询所有

findone

  直接加载

getone

  底层是getrefresh()。是延迟加载的

save:

      save方法保存的对象主键为null,视为保存。主键存在值,视为修改

 

 

继承jpaspecificationexecutor的方法列表

   继承了以 specification 为参数的动态查询方法

3.2   使用jpql的方式查询

使用spring data jpa提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用@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.3   使用sql语句查询

spring data jpa同样也支持sql语句的查询,如下:

 

    /**

     * nativequery : 使用本地sql的方式查询

     */

    @query(value="select * from cst_customer",nativequery=true)

    public void findsql();

 

3.4   方法命名规则查询

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

 

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

 

    //方法命名方式查询(根据客户名称查询客户)

    public customer findbycustname(string custname);

 

 

具体的关键字,使用方法和生产成sql如下表所示

 

         

keyword

sample

jpql

   

and

findbylastnameandfirstname

… where x.lastname = ?1 and x.firstname = ?2

   

or

findbylastnameorfirstname

… where x.lastname = ?1 or x.firstname = ?2

   

is,equals

findbyfirstnameis,

findbyfirstnameequals

… where x.firstname = ?1

   

between

findbystartdatebetween

… where x.startdate between ?1 and ?2

   

lessthan

findbyagelessthan

… where x.age < ?1

   

lessthanequal

findbyagelessthanequal

… where x.age ⇐ ?1

   

greaterthan

findbyagegreaterthan

… where x.age > ?1

   

greaterthanequal

findbyagegreaterthanequal

… where x.age >= ?1

   

after

findbystartdateafter

… where x.startdate > ?1

   

before

findbystartdatebefore

… where x.startdate < ?1

   

isnull

findbyageisnull

… where x.age is null

   

isnotnull,notnull

findbyage(is)notnull

… where x.age not null

   

like

findbyfirstnamelike

… where x.firstname like ?1

   

notlike

findbyfirstnamenotlike

… where x.firstname not like ?1

   

startingwith

findbyfirstnamestartingwith

… where x.firstname like ?1 (parameter bound with appended %)

   

endingwith

findbyfirstnameendingwith

… where x.firstname like ?1 (parameter bound with prepended %)

   

containing

findbyfirstnamecontaining

… where x.firstname like ?1 (parameter bound wrapped in %)

   

orderby

findbyageorderbylastnamedesc

… where x.age = ?1 order by x.lastname desc

   

not

findbylastnamenot

… where x.lastname <> ?1

   

in

findbyagein(collection ages)

… where x.age in ?1

   

notin

findbyagenotin(collection age)

… where x.age not in ?1

   

true

findbyactivetrue()

… where x.active = true

   

false

findbyactivefalse()

… where x.active = false

   

ignorecase

findbyfirstnameignorecase

… where upper(x.firstame) = upper(?1)