文章目录
回顾第一个主要学习: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"/>
<!--<!– 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(* 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第二天算是结束了,下一篇有关得则是最后一天入门项目了,谢谢观看