三天快速入门Spring Data JPA之day02

it2025-05-20  9

文章目录

第1章Spring Data JPA的概述1.1概述简介1.2Spring Data JPA的特性1.3Spring Data JPA 与 JPA和hibernate之间的关系 第2章Spring Data JPA的快速入门2.1需求说明完成增删改查操作环境搭建测试增删改查保存更新用户删除 第3章Spring Data JPA的内部原理剖析条件查询案例统计查询传入id查询账号是否存在getOne进行id查询 第4章Spring Data JPA复杂查询JpaSpecificationExecutor使用占位符查询单占位符多占位符 使用JPQL更新使用原生SQL查询查所有模糊查询 使用命名查询 回顾第一个主要学习:orm思想,hibernate,JPA的相关操作等操作。 第二天安排就是正式入门Spring Data JPA了。首先当然是上官网啦!

https://spring.io/projects/spring-data-jpa 最新官方稳定版文档地址: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#preface

历史版本文档查找地址: https://docs.spring.io/spring-data/jpa/docs/

第1章Spring Data JPA的概述

1.1概述简介

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

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

1.2Spring Data JPA的特性

SpringData Jpa 极大简化了数据库访问层代码。 如何简化的呢? 使用SpringDataJpa,我们的dao层中只需要写接口,就自动具有了增删改查、分页查询等方法。

1.3Spring Data JPA 与 JPA和hibernate之间的关系

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

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

第2章Spring Data JPA的快速入门

2.1需求说明完成增删改查操作

实现步骤:

i.搭建环境 - 创建工程导入坐标 - 配置spring的配置文件(配置spring Data jpa的整合) - 编写实体类(Customer),使用jpa注解配置映射关系 ii.编写一个符合springDataJpa的dao层接口 - 只需要编写dao层接口,不需要编写dao层接口的实现类 - dao层接口规范 1.需要继承两个接口(JpaRepository,JpaSpecificationExecutor) 2.需要提供响应的泛型

环境搭建

如果和上次的工程放在一起,则创建一个module,不依赖任何工程,命名为jpa_day2 按下图目录结构补充:

applicationContext.xml如下:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <!--spring和spring data jpa的配置--> <!-- 1.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="root3306" /> </bean> <!--2.创建entityManagerFactory对象交给spring容器管理--> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <!--配置扫描的包--> <property name="packagesToScan" value="com.tho.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> <!-- 3.事务管理器--> <!-- JPA事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <!--整合spring data Jpa--> <jpa:repositories base-package="com.tho.dao" transaction-manager-ref="transactionManager" entity-manager-factory-ref="entityManagerFactory"/> <!--&lt;!&ndash; 4.txAdvice&ndash;&gt;--> <!--<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>--> <!--&lt;!&ndash; 5.aop&ndash;&gt;--> <!--<aop:config>--> <!--<aop:pointcut id="pointcut" expression="execution(* com.tho.service.*.*(..))" />--> <!--<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />--> <!--</aop:config>--> <!----> <context:component-scan base-package="com.tho"/> </beans>

pom.xml

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.tho</groupId> <artifactId>jpa_day2</artifactId> <version>1.0-SNAPSHOT</version> <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> <!--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> <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> </project>

Customer实体类:

package com.tho.domain; import javax.persistence.*; /** * * * 所有的注解都是使用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_address")//指定和表中cust_address字段的映射关系 private String custAddress; @Column(name="cust_industry")//指定和表中cust_industry字段的映射关系 private String custIndustry; @Column(name="cust_level")//指定和表中cust_level字段的映射关系 private String custLevel; @Column(name="cust_name") //指定和表中cust_name字段的映射关系 private String custName; @Column(name="cust_phone")//指定和表中cust_phone字段的映射关系 private String custPhone; @Column(name="cust_source")//指定和表中cust_source字段的映射关系 private String custSource; public Long getCustId() { return custId; } public void setCustId(Long custId) { this.custId = custId; } public String getCustAddress() { return custAddress; } public void setCustAddress(String custAddress) { this.custAddress = custAddress; } 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 getCustName() { return custName; } public void setCustName(String custName) { this.custName = custName; } public String getCustPhone() { return custPhone; } public void setCustPhone(String custPhone) { this.custPhone = custPhone; } public String getCustSource() { return custSource; } public void setCustSource(String custSource) { this.custSource = custSource; } public Customer(String custAddress, String custIndustry, String custLevel, String custName, String custPhone, String custSource) { this.custAddress = custAddress; this.custIndustry = custIndustry; this.custLevel = custLevel; this.custName = custName; this.custPhone = custPhone; this.custSource = custSource; } public Customer() { } @Override public String toString() { return "Customer{" + "custId=" + custId + ", custAddress='" + custAddress + '\'' + ", custIndustry='" + custIndustry + '\'' + ", custLevel='" + custLevel + '\'' + ", custName='" + custName + '\'' + ", custPhone='" + custPhone + '\'' + ", custSource='" + custSource + '\'' + '}'; } }

库表还是那一样,jpa库,cst_customer: 建表脚本如下:

/*创建客户表*/ CREATE TABLE cst_customer ( cust_id bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)', cust_name varchar(32) NOT NULL COMMENT '客户名称(公司名称)', cust_source varchar(32) DEFAULT NULL COMMENT '客户信息来源', cust_industry varchar(32) DEFAULT NULL COMMENT '客户所属行业', cust_level varchar(32) DEFAULT NULL COMMENT '客户级别', cust_address varchar(128) DEFAULT NULL COMMENT '客户联系地址', cust_phone varchar(64) DEFAULT NULL COMMENT '客户联系电话', PRIMARY KEY (`cust_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

继承spring data jpa接口,完成数据操控层编写CustomerDao:

package com.tho.dao; import com.tho.domain.Customer; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * JpaRepository<实体类类型,主键类型>:用来完成基本CRUD操作 * JpaSpecificationExecutor<实体类类型>:用于复杂查询(分页等查询操作) * 同时记住是接口,动态代理的都是接口 */ public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> { }

测试增删改查

然后在test根目录下创建测试类 编写测试类:

package com.tho.test; import com.tho.dao.CustomerDao; import com.tho.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; @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(1L); System.out.println(customer); } }

根据id查询成功

保存

增加保存方法:

/** *save方法:用于保存或者更新 *保存操作,根据是否有id来判断,保存还是更新 *没查到id就是保存 */ @Test public void testSave(){ Customer customer=new Customer(); customer.setCustName("jpa学习者"); customer.setCustLevel("worker"); customerDao.save(customer); System.out.println(customer); }

出现乱码,不要紧,不是重点,首先是正常插入成功的,ok

更新用户

更新方法:

/** *更新操作,也是save方法,根据是否有id来判断保存还是更新 * 有相应id则是更新用户数据 */ @Test public void testUpdate(){ Customer customer=new Customer(); customer.setCustId(4l); customer.setCustName("jpa"); customer.setCustPhone("223344"); customerDao.save(customer); System.out.println(customer); }

删除

根据id删除:

/** * 删除方法 */ @Test public void testDelete(){ customerDao.delete(4l); }

其实我们没做什么,只是用CustomerDao调用方法即完成了增删改查,这其实是通过继承JpaRepository接口实现的。我们点进去看这个接口的方法吧。

// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.data.jpa.repository; import java.io.Serializable; import java.util.List; import org.springframework.data.domain.Sort; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.PagingAndSortingRepository; @NoRepositoryBean public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> { List<T> findAll(); List<T> findAll(Sort var1); List<T> findAll(Iterable<ID> var1); <S extends T> List<S> save(Iterable<S> var1); void flush(); <S extends T> S saveAndFlush(S var1); void deleteInBatch(Iterable<T> var1); void deleteAllInBatch(); T getOne(ID var1); }

看着很少也没看见我们调用的方法,所以我们推测这些方法也是它继承回来的。使用idea快捷键看看,选中这个JpaRepository。ctrl+F12查看:

他的方法就全部出现了,然后我们注意到我们上面使用的这些方法其实是继承自CrudRepository,上源码:

/* * Copyright 2008-2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.data.repository; import java.io.Serializable; /** * Interface for generic CRUD operations on a repository for a specific type. * * @author Oliver Gierke * @author Eberhard Wolff */ @NoRepositoryBean public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> { /** * Saves a given entity. Use the returned instance for further operations as the save operation might have changed the * entity instance completely. * * @param entity * @return the saved entity */ <S extends T> S save(S entity); /** * Saves all given entities. * * @param entities * @return the saved entities * @throws IllegalArgumentException in case the given entity is {@literal null}. */ <S extends T> Iterable<S> save(Iterable<S> entities); /** * Retrieves an entity by its id. * * @param id must not be {@literal null}. * @return the entity with the given id or {@literal null} if none found * @throws IllegalArgumentException if {@code id} is {@literal null} */ T findOne(ID id); /** * Returns whether an entity with the given id exists. * * @param id must not be {@literal null}. * @return true if an entity with the given id exists, {@literal false} otherwise * @throws IllegalArgumentException if {@code id} is {@literal null} */ boolean exists(ID id); /** * Returns all instances of the type. * * @return all entities */ Iterable<T> findAll(); /** * Returns all instances of the type with the given IDs. * * @param ids * @return */ Iterable<T> findAll(Iterable<ID> ids); /** * Returns the number of entities available. * * @return the number of entities */ long count(); /** * Deletes the entity with the given id. * * @param id must not be {@literal null}. * @throws IllegalArgumentException in case the given {@code id} is {@literal null} */ void delete(ID id); /** * Deletes a given entity. * * @param entity * @throws IllegalArgumentException in case the given entity is {@literal null}. */ void delete(T entity); /** * Deletes the given entities. * * @param entities * @throws IllegalArgumentException in case the given {@link Iterable} is {@literal null}. */ void delete(Iterable<? extends T> entities); /** * Deletes all entities managed by the repository. */ void deleteAll(); }

接下来继续写查询所有方法:

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

至此基本的单表增删改查,基本就操作完了。

findOne(id) :根据id查询 save(customer):保存或者更新(依据:传递的实体类对象中,是否包含id属性) delete(id) :根据id删除 findAll() : 查询全部

第3章Spring Data JPA的内部原理剖析

我们回过头来打断点看看我们在调用方法前,生成的对象是什么,我们明明都没什么都没做也能执行:

通过上图我们发现,同样的,在调用方法前使用的是JDK动态代理模式生成的也是代理对象,通过代理接口,增强功能。

可以右键查看源码

/* * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.aop.framework; import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.List; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.AopInvocationException; import org.springframework.aop.RawTargetAccess; import org.springframework.aop.TargetSource; import org.springframework.aop.support.AopUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** * JDK-based {@link AopProxy} implementation for the Spring AOP framework, * based on JDK {@link java.lang.reflect.Proxy dynamic proxies}. * * <p>Creates a dynamic proxy, implementing the interfaces exposed by * the AopProxy. Dynamic proxies <i>cannot</i> be used to proxy methods * defined in classes, rather than interfaces. * * <p>Objects of this type should be obtained through proxy factories, * configured by an {@link AdvisedSupport} class. This class is internal * to Spring's AOP framework and need not be used directly by client code. * * <p>Proxies created using this class will be thread-safe if the * underlying (target) class is thread-safe. * * <p>Proxies are serializable so long as all Advisors (including Advices * and Pointcuts) and the TargetSource are serializable. * * @author Rod Johnson * @author Juergen Hoeller * @author Rob Harrop * @author Dave Syer * @see java.lang.reflect.Proxy * @see AdvisedSupport * @see ProxyFactory */ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable { /** use serialVersionUID from Spring 1.2 for interoperability */ private static final long serialVersionUID = 5531744639992436476L; /* * NOTE: We could avoid the code duplication between this class and the CGLIB * proxies by refactoring "invoke" into a template method. However, this approach * adds at least 10% performance overhead versus a copy-paste solution, so we sacrifice * elegance for performance. (We have a good test suite to ensure that the different * proxies behave the same :-) * This way, we can also more easily take advantage of minor optimizations in each class. */ /** We use a static Log to avoid serialization issues */ private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class); /** Config used to configure this proxy */ private final AdvisedSupport advised; /** * Is the {@link #equals} method defined on the proxied interfaces? */ private boolean equalsDefined; /** * Is the {@link #hashCode} method defined on the proxied interfaces? */ private boolean hashCodeDefined; /** * Construct a new JdkDynamicAopProxy for the given AOP configuration. * @param config the AOP configuration as AdvisedSupport object * @throws AopConfigException if the config is invalid. We try to throw an informative * exception in this case, rather than let a mysterious failure happen later. */ public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException { Assert.notNull(config, "AdvisedSupport must not be null"); if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) { throw new AopConfigException("No advisors and no TargetSource specified"); } this.advised = config; } @Override public Object getProxy() { return getProxy(ClassUtils.getDefaultClassLoader()); } @Override public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); } Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); } /** * Finds any {@link #equals} or {@link #hashCode} method that may be defined * on the supplied set of interfaces. * @param proxiedInterfaces the interfaces to introspect */ private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) { for (Class<?> proxiedInterface : proxiedInterfaces) { Method[] methods = proxiedInterface.getDeclaredMethods(); for (Method method : methods) { if (AopUtils.isEqualsMethod(method)) { this.equalsDefined = true; } if (AopUtils.isHashCodeMethod(method)) { this.hashCodeDefined = true; } if (this.equalsDefined && this.hashCodeDefined) { return; } } } } /** * Implementation of {@code InvocationHandler.invoke}. * <p>Callers will see exactly the exception thrown by the target, * unless a hook method throws an exception. */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Class<?> targetClass = null; Object target = null; try { if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } Object retVal; if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // May be null. Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. target = targetSource.getTarget(); if (target != null) { targetClass = target.getClass(); } // Get the interception chain for this method. List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // We need to create a method invocation... invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); } // Massage return value if necessary. Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } } /** * Equality means interfaces, advisors and TargetSource are equal. * <p>The compared object may be a JdkDynamicAopProxy instance itself * or a dynamic proxy wrapping a JdkDynamicAopProxy instance. */ @Override public boolean equals(Object other) { if (other == this) { return true; } if (other == null) { return false; } JdkDynamicAopProxy otherProxy; if (other instanceof JdkDynamicAopProxy) { otherProxy = (JdkDynamicAopProxy) other; } else if (Proxy.isProxyClass(other.getClass())) { InvocationHandler ih = Proxy.getInvocationHandler(other); if (!(ih instanceof JdkDynamicAopProxy)) { return false; } otherProxy = (JdkDynamicAopProxy) ih; } else { // Not a valid comparison... return false; } // If we get here, otherProxy is the other AopProxy. return AopProxyUtils.equalsInProxy(this.advised, otherProxy.advised); } /** * Proxy uses the hash code of the TargetSource. */ @Override public int hashCode() { return JdkDynamicAopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode(); } }

找到具体的处理代理的方法打个断点重新调一下,F8一路走下去

回头看赋值是否已经变为SimpleJpaRepository 右键进入查看SimpleJpaRepository:

/* * Copyright 2008-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.data.jpa.repository.support; import static org.springframework.data.jpa.repository.query.QueryUtils.*; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.persistence.EntityManager; import javax.persistence.LockModeType; import javax.persistence.NoResultException; import javax.persistence.Parameter; import javax.persistence.Query; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.ParameterExpression; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.provider.PersistenceProvider; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.query.Jpa21Utils; import org.springframework.data.jpa.repository.query.JpaEntityGraph; import org.springframework.data.jpa.repository.query.QueryUtils; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; /** * Default implementation of the {@link org.springframework.data.repository.CrudRepository} interface. This will offer * you a more sophisticated interface than the plain {@link EntityManager} . * * @author Oliver Gierke * @author Eberhard Wolff * @author Thomas Darimont * @param <T> the type of the entity to handle * @param <ID> the type of the entity's identifier */ @Repository @Transactional(readOnly = true) public class SimpleJpaRepository<T, ID extends Serializable> implements JpaRepository<T, ID>, JpaSpecificationExecutor<T> { private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null!"; private final JpaEntityInformation<T, ?> entityInformation; private final EntityManager em; private final PersistenceProvider provider; private CrudMethodMetadata metadata; /** * Creates a new {@link SimpleJpaRepository} to manage objects of the given {@link JpaEntityInformation}. * * @param entityInformation must not be {@literal null}. * @param entityManager must not be {@literal null}. */ public SimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) { Assert.notNull(entityInformation); Assert.notNull(entityManager); this.entityInformation = entityInformation; this.em = entityManager; this.provider = PersistenceProvider.fromEntityManager(entityManager); } /** * Creates a new {@link SimpleJpaRepository} to manage objects of the given domain type. * * @param domainClass must not be {@literal null}. * @param em must not be {@literal null}. */ public SimpleJpaRepository(Class<T> domainClass, EntityManager em) { this(JpaEntityInformationSupport.getEntityInformation(domainClass, em), em); } /** * Configures a custom {@link CrudMethodMetadata} to be used to detect {@link LockModeType}s and query hints to be * applied to queries. * * @param crudMethodMetadata */ public void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) { this.metadata = crudMethodMetadata; } protected CrudMethodMetadata getRepositoryMethodMetadata() { return metadata; } protected Class<T> getDomainClass() { return entityInformation.getJavaType(); } private String getDeleteAllQueryString() { return getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()); } private String getCountQueryString() { String countQuery = String.format(COUNT_QUERY_STRING, provider.getCountQueryPlaceholder(), "%s"); return getQueryString(countQuery, entityInformation.getEntityName()); } /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable) */ @Transactional public void delete(ID id) { Assert.notNull(id, ID_MUST_NOT_BE_NULL); T entity = findOne(id); if (entity == null) { throw new EmptyResultDataAccessException(String.format("No %s entity with id %s exists!", entityInformation.getJavaType(), id), 1); } delete(entity); } /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object) */ @Transactional public void delete(T entity) { Assert.notNull(entity, "The entity must not be null!"); em.remove(em.contains(entity) ? entity : em.merge(entity)); } /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable) */ @Transactional public void delete(Iterable<? extends T> entities) { Assert.notNull(entities, "The given Iterable of entities not be null!"); for (T entity : entities) { delete(entity); } } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaRepository#deleteInBatch(java.lang.Iterable) */ @Transactional public void deleteInBatch(Iterable<T> entities) { Assert.notNull(entities, "The given Iterable of entities not be null!"); if (!entities.iterator().hasNext()) { return; } applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()), entities, em) .executeUpdate(); } /* * (non-Javadoc) * @see org.springframework.data.repository.Repository#deleteAll() */ @Transactional public void deleteAll() { for (T element : findAll()) { delete(element); } } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaRepository#deleteAllInBatch() */ @Transactional public void deleteAllInBatch() { em.createQuery(getDeleteAllQueryString()).executeUpdate(); } /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#findOne(java.io.Serializable) */ public T findOne(ID id) { Assert.notNull(id, ID_MUST_NOT_BE_NULL); Class<T> domainType = getDomainClass(); if (metadata == null) { return em.find(domainType, id); } LockModeType type = metadata.getLockModeType(); Map<String, Object> hints = getQueryHints(); return type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints); } /** * Returns a {@link Map} with the query hints based on the current {@link CrudMethodMetadata} and potential * {@link EntityGraph} information. * * @return */ protected Map<String, Object> getQueryHints() { if (metadata.getEntityGraph() == null) { return metadata.getQueryHints(); } Map<String, Object> hints = new HashMap<String, Object>(); hints.putAll(metadata.getQueryHints()); hints.putAll(Jpa21Utils.tryGetFetchGraphHints(em, getEntityGraph(), getDomainClass())); return hints; } private JpaEntityGraph getEntityGraph() { String fallbackName = this.entityInformation.getEntityName() + "." + metadata.getMethod().getName(); return new JpaEntityGraph(metadata.getEntityGraph(), fallbackName); } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaRepository#getOne(java.io.Serializable) */ @Override public T getOne(ID id) { Assert.notNull(id, ID_MUST_NOT_BE_NULL); return em.getReference(getDomainClass(), id); } /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#exists(java.io.Serializable) */ public boolean exists(ID id) { Assert.notNull(id, ID_MUST_NOT_BE_NULL); if (entityInformation.getIdAttribute() == null) { return findOne(id) != null; } String placeholder = provider.getCountQueryPlaceholder(); String entityName = entityInformation.getEntityName(); Iterable<String> idAttributeNames = entityInformation.getIdAttributeNames(); String existsQuery = QueryUtils.getExistsQueryString(entityName, placeholder, idAttributeNames); TypedQuery<Long> query = em.createQuery(existsQuery, Long.class); if (!entityInformation.hasCompositeId()) { query.setParameter(idAttributeNames.iterator().next(), id); return query.getSingleResult() == 1L; } for (String idAttributeName : idAttributeNames) { Object idAttributeValue = entityInformation.getCompositeIdAttributeValue(id, idAttributeName); boolean complexIdParameterValueDiscovered = idAttributeValue != null && !query.getParameter(idAttributeName).getParameterType().isAssignableFrom(idAttributeValue.getClass()); if (complexIdParameterValueDiscovered) { // fall-back to findOne(id) which does the proper mapping for the parameter. return findOne(id) != null; } query.setParameter(idAttributeName, idAttributeValue); } return query.getSingleResult() == 1L; } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaRepository#findAll() */ public List<T> findAll() { return getQuery(null, (Sort) null).getResultList(); } /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#findAll(ID[]) */ public List<T> findAll(Iterable<ID> ids) { if (ids == null || !ids.iterator().hasNext()) { return Collections.emptyList(); } if (entityInformation.hasCompositeId()) { List<T> results = new ArrayList<T>(); for (ID id : ids) { results.add(findOne(id)); } return results; } ByIdsSpecification<T> specification = new ByIdsSpecification<T>(entityInformation); TypedQuery<T> query = getQuery(specification, (Sort) null); return query.setParameter(specification.parameter, ids).getResultList(); } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaRepository#findAll(org.springframework.data.domain.Sort) */ public List<T> findAll(Sort sort) { return getQuery(null, sort).getResultList(); } /* * (non-Javadoc) * @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Pageable) */ public Page<T> findAll(Pageable pageable) { if (null == pageable) { return new PageImpl<T>(findAll()); } return findAll(null, pageable); } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findOne(org.springframework.data.jpa.domain.Specification) */ public T findOne(Specification<T> spec) { try { return getQuery(spec, (Sort) null).getSingleResult(); } catch (NoResultException e) { return null; } } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification) */ public List<T> findAll(Specification<T> spec) { return getQuery(spec, (Sort) null).getResultList(); } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification, org.springframework.data.domain.Pageable) */ public Page<T> findAll(Specification<T> spec, Pageable pageable) { TypedQuery<T> query = getQuery(spec, pageable); return pageable == null ? new PageImpl<T>(query.getResultList()) : readPage(query, pageable, spec); } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification, org.springframework.data.domain.Sort) */ public List<T> findAll(Specification<T> spec, Sort sort) { return getQuery(spec, sort).getResultList(); } /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#count() */ public long count() { return em.createQuery(getCountQueryString(), Long.class).getSingleResult(); } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#count(org.springframework.data.jpa.domain.Specification) */ public long count(Specification<T> spec) { return executeCountQuery(getCountQuery(spec)); } /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object) */ @Transactional public <S extends T> S save(S entity) { if (entityInformation.isNew(entity)) { em.persist(entity); return entity; } else { return em.merge(entity); } } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaRepository#saveAndFlush(java.lang.Object) */ @Transactional public <S extends T> S saveAndFlush(S entity) { S result = save(entity); flush(); return result; } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaRepository#save(java.lang.Iterable) */ @Transactional public <S extends T> List<S> save(Iterable<S> entities) { List<S> result = new ArrayList<S>(); if (entities == null) { return result; } for (S entity : entities) { result.add(save(entity)); } return result; } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaRepository#flush() */ @Transactional public void flush() { em.flush(); } /** * Reads the given {@link TypedQuery} into a {@link Page} applying the given {@link Pageable} and * {@link Specification}. * * @param query must not be {@literal null}. * @param spec can be {@literal null}. * @param pageable can be {@literal null}. * @return */ protected Page<T> readPage(TypedQuery<T> query, Pageable pageable, Specification<T> spec) { query.setFirstResult(pageable.getOffset()); query.setMaxResults(pageable.getPageSize()); Long total = executeCountQuery(getCountQuery(spec)); List<T> content = total > pageable.getOffset() ? query.getResultList() : Collections.<T> emptyList(); return new PageImpl<T>(content, pageable, total); } /** * Creates a new {@link TypedQuery} from the given {@link Specification}. * * @param spec can be {@literal null}. * @param pageable can be {@literal null}. * @return */ protected TypedQuery<T> getQuery(Specification<T> spec, Pageable pageable) { Sort sort = pageable == null ? null : pageable.getSort(); return getQuery(spec, sort); } /** * Creates a {@link TypedQuery} for the given {@link Specification} and {@link Sort}. * * @param spec can be {@literal null}. * @param sort can be {@literal null}. * @return */ protected TypedQuery<T> getQuery(Specification<T> spec, Sort sort) { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<T> query = builder.createQuery(getDomainClass()); Root<T> root = applySpecificationToCriteria(spec, query); query.select(root); if (sort != null) { query.orderBy(toOrders(sort, root, builder)); } return applyRepositoryMethodMetadata(em.createQuery(query)); } /** * Creates a new count query for the given {@link Specification}. * * @param spec can be {@literal null}. * @return */ protected TypedQuery<Long> getCountQuery(Specification<T> spec) { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<Long> query = builder.createQuery(Long.class); Root<T> root = applySpecificationToCriteria(spec, query); if (query.isDistinct()) { query.select(builder.countDistinct(root)); } else { query.select(builder.count(root)); } return em.createQuery(query); } /** * Applies the given {@link Specification} to the given {@link CriteriaQuery}. * * @param spec can be {@literal null}. * @param query must not be {@literal null}. * @return */ private <S> Root<T> applySpecificationToCriteria(Specification<T> spec, CriteriaQuery<S> query) { Assert.notNull(query); Root<T> root = query.from(getDomainClass()); if (spec == null) { return root; } CriteriaBuilder builder = em.getCriteriaBuilder(); Predicate predicate = spec.toPredicate(root, query, builder); if (predicate != null) { query.where(predicate); } return root; } private TypedQuery<T> applyRepositoryMethodMetadata(TypedQuery<T> query) { if (metadata == null) { return query; } LockModeType type = metadata.getLockModeType(); TypedQuery<T> toReturn = type == null ? query : query.setLockMode(type); applyQueryHints(toReturn); return toReturn; } private void applyQueryHints(Query query) { for (Entry<String, Object> hint : getQueryHints().entrySet()) { query.setHint(hint.getKey(), hint.getValue()); } } /** * Executes a count query and transparently sums up all values returned. * * @param query must not be {@literal null}. * @return */ private static Long executeCountQuery(TypedQuery<Long> query) { Assert.notNull(query); List<Long> totals = query.getResultList(); Long total = 0L; for (Long element : totals) { total += element == null ? 0 : element; } return total; } /** * Specification that gives access to the {@link Parameter} instance used to bind the ids for * {@link SimpleJpaRepository#findAll(Iterable)}. Workaround for OpenJPA not binding collections to in-clauses * correctly when using by-name binding. * * @see https://issues.apache.org/jira/browse/OPENJPA-2018?focusedCommentId=13924055 * @author Oliver Gierke */ @SuppressWarnings("rawtypes") private static final class ByIdsSpecification<T> implements Specification<T> { private final JpaEntityInformation<T, ?> entityInformation; ParameterExpression<Iterable> parameter; public ByIdsSpecification(JpaEntityInformation<T, ?> entityInformation) { this.entityInformation = entityInformation; } /* * (non-Javadoc) * @see org.springframework.data.jpa.domain.Specification#toPredicate(javax.persistence.criteria.Root, javax.persistence.criteria.CriteriaQuery, javax.persistence.criteria.CriteriaBuilder) */ public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) { Path<?> path = root.get(entityInformation.getIdAttribute()); parameter = cb.parameter(Iterable.class); return path.in(parameter); } } }

阅读具体代码,确实有我们刚刚使用的方法的实现,那是不是说明具体的实现就是SimpleJpaRepository完成的呢?再加个断点再findOne方法里测试: 出现了确实是我们传的参数!继续走 其实还是使用了jpa的底层,使用EntityManager调用方法。 总结: spring data jpa是spring通过自身的aop机制,采用的JDK动态代理,代理的我们dao层继承接口的类,而通过其接口实现类SimpleJpaRepository,内部调用jpa规范种的EntityManager通过hibernate的实现完成使用简单的增删改查操作。 看完图再次回忆一下springDataJpa的运行过程和原理剖析: 1.通过JdkDynamicAopProxy的invoke方法创建了一个动态代理对象 2.SimpleJpaRepository当中封装了JPA的操作(借助JPA的api完成数据库的CRUD) 3.通过hibernate完成数据库操作(封装了jdbc)

条件查询案例

统计查询

/** * 统计查询 */ @Test public void TestCount(){ long count = customerDao.count(); System.out.println(count); }

传入id查询账号是否存在

/** * 测试:判断id为4的客户是否存在 * 1.可以查询id为4的客户 * 如果为空,代表不存在,如果不为空,代表存在 * 2.判断数据库种id为4的客户的数量 * 如果数为0,代表不存在,如果大于0,代表存在 * spring data jpa 直接调用exists方法传入id值,根据布尔值确定 */ @Test public void TestExist(){ boolean customer = customerDao.exists(4l); System.out.println(customer); }

getOne进行id查询

/** * 测试getOne方法 * 根据id从数据库查询 * @Transactional:保证getOne正常运行 */ @Test @Transactional public void testGetOne(){ Customer customer = customerDao.getOne(3l); System.out.println(customer); }

可见getOne是采取懒加载或者说延迟加载的方式,返回的是客户的动态代理对象,什么时候调用就是什么时候去加载查询,如果不用就不查

第4章Spring Data JPA复杂查询

JpaSpecificationExecutor

关于JpaSpecificationExecutor直接翻译是JPA详细执行器,其实就是JPA复杂条件执行器的意思:

回顾一下方法调用已经学习了,那么不要忘了jpql:

i.借助接口中的定义好的方法完成查询 findOne(id):根据id查询 ii.jpql的查询方式 jpql : jpa query language (jpq查询语言) 特点:语法或关键字和sql语句类似 查询的是类和类中的属性

相信大多数人学习此技术前都学习过Mybatis,那么我们来根据Mybatis的注解开发来推测一下Spring Data JPA中使用JPQL的步骤吧:

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

使用占位符查询

那就开始测试这几个方法吧:在CustomerDao中加入带有jpql注解的方法,然后再创一个新的测试类测试。

单占位符
package com.tho.dao; import com.tho.domain.Customer; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; /** * JpaRepository<实体类类型,主键类型>:用来完成基本CRUD操作 * JpaSpecificationExecutor<实体类类型>:用于复杂查询(分页等查询操作) * 同时记住是接口,动态代理的都是接口 */ public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> { @Query(value = "from Customer where custName = ?") public Customer findJpql(String custName); } package com.tho.test; import com.tho.dao.CustomerDao; import com.tho.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; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class JpqlTest { @Autowired private CustomerDao customerDao; @Test public void testFindJpql(){ Customer customer = customerDao.findJpql("calmtho"); System.out.println(customer); } }

多占位符

CustomerDao:

/** * 案例:根据客户名称和客户id查询客户 * jpql:from Customer where customer=? and custId=? */ @Query(value = "from Customer where custName=? and custId=?") public Customer findCustNameAndId(String name,Long id);

JpqlTest:

@Test public void testfindCustNameAndId(){ Customer customer = customerDao.findCustNameAndId("calm",1l); System.out.println(customer); }

注意多占位符默认是映射是和参数传输的位置一致,名字不要求,但是类型一定要一致。而且想保证数据不出差,统一类型必须注意顺序一致,调换位置测试可知: 通过指定占位符顺序来指定,占位符起始索引为1: CustomerDao:

/** * 案例:根据客户名称和客户id查询客户 * jpql:from Customer where customer=? and custId=? * * @Query(value = "from Customer where custName=? and custId=?") * public Customer findCustNameAndId(Long id,String name);//占位符不对应导致的错误写法 * 修正方法则是使用占位符指定参数位置来确定 */ @Query(value = "from Customer where custName=?2 and custId=?1") public Customer findCustNameAndId(Long id,String name);

使用JPQL更新

CustomerDao接口编写方法:

/** * 案例: 使用JPQL条件查询id为1的用户,并更新客户电话为020 * sql: upadate cst_customer set cust_phone=? where cust_id=? * jpql:update Customer set custPhone=?2 where custId=?1 */ @Query(value = "update Customer set custPhone=?2 where custId=?1") @Modifying public Customer updateCustomer(Long id,String phone);

添加测试方法:

@Test public void testUpdateCustomer(){ customerDao.updateCustomer(1l,"020"); }

报错说更新操作只能使用void类型或者Integer类型,将接口方法类型修改为void

debug运行,又报错,我们来看看, 噢原来没加事务导致

加上测试@Transactional: jpql执行了,可是结果没变化!!为什么呢?回想一下事务的相关,噢,可能是没提交,自动回滚了。没错当我们没配置回滚参数的话,默认是自动回滚的。修改一下:

@Test @Transactional @Rollback(value=false) public void testUpdateCustomer(){ customerDao.updateCustomer(1l,"020"); }

使用原生SQL查询

查所有

CustomerDao接口方法:

/** * 使用sql的形式查询 * 查询全部的客户 * sql:select * from cst_customer; * nativeQuery = true: sql查询 * false : jpql查询 */ @Query(value="select * from cst_customer",nativeQuery = true) public List<Object[]>findSql();

JpqlTest方法

@Test public void testFindSql(){ List<Object[]> list = customerDao.findSql(); for (Object[] obj:list){ System.out.println(Arrays.toString(obj)); } }

debug运行:

模糊查询

CustomerDao接口:

@Query(value = "select * from cst_customer where cust_name like ?1",nativeQuery = true) public List<Object[]>findSql(String name);

JpqlTest:

@Test public void findBySql(){ List<Object[]> list = customerDao.findSql("calm%"); for (Object[] o:list) { System.out.println(Arrays.toString(o)); } }

使用命名查询

1.单条件查询

方法名称规则查询 是对jpql查询,更加深入一层的封装 我们只需要按照SpringDataJpa提供的方法名称规则定义方法,不需要再配置jpql语句,完成查询 findBy开头: 代表查询 对象中属性名称(首字母大写) 含义:根据属性名称进行查询

CustomerDao接口方法:不要写jpql

/** * 方法名的约定: * findBy : 查询 * 对象中的属性名(首字母大写) : 查询条件 * 如我们映射了的属性:CustName * findByCustName - - 根据客户名称查询 * * 再springdataJpa得运行阶段 * 会根据方法名称进行解析 findBy from xxx(实体类) * 属性名称 where custName */ public Customer findByCustName(String custName);//传参名字任意,看得懂,类型一致即可 }

Jpql测试类:

//测试方法命名规则得查询 @Test public void testNaming(){ Customer customer = customerDao.findByCustName("calmtho"); System.out.println(customer); }

2.模糊查询 findBy + 属性名称+‘查询方式(Like|isnull)

//传参名字任意,看得懂,类型一致即可 public List<Customer> findByCustNameLike(String name); @Test public void testFindByCustNameLike(){ List<Customer> list = customerDao.findByCustNameLike("calm%"); for (Customer c:list){ System.out.println(c); } }

3.多条件查询 findBy +属性名+“查询方式”+“多条件得连接符(and|or)”+属性名+“查询方式” 以下是具体案例 CustomerDao接口方法:

/使用客户名称模糊匹配和客户所属行业精准匹配,这个参传顺序必须一致,类型必须对应,参数名任意 public Customer findByCustNameLikeAndCustIndustry(String name,String industry);

JpqlTest:

@Test public void testNameLikeAnd(){ Customer c = customerDao.findByCustNameLikeAndCustIndustry("calm%", "996"); System.out.println(c); }

ok,基本上做到这里已经掌握通过findBy拼接命名得方法查询 最后回忆一下这几种拼接得写法: findBy + 属性名称(根据属性名称进行完成匹配得查询) - -精准匹配 findBy +属性名+”查询方式“(即Like,isnull等) indBy +属性名+“查询方式”+“多条件得连接符(and|or)”+属性名+“查询方式” 最后切记这些方法需要传参得话,必须必须参数类型一致,以及参数顺序一致

ok第二天算是结束了,下一篇有关得则是最后一天入门项目了,谢谢观看

最新回复(0)