前言
spring Data
项目的目的是为了简化构建基于 Spring 框架应用的数据访问计数,使数据库访问变得方便快捷。包括非关系数据库
、Map-Reduce
框架、云数据服务
等等,另外也包含对关系数据库
的访问支持。
JPA诞生的缘由是为了整合第三方ORM框架,建立一种标准的方式
SpringDataJPA是一个JPA数据访问抽象,需要第三方实现,有各种实现JPA的提供者。
Spring Data JPA
是Spring提供的持久层的解决方案Spring Data JPA
其实就是Spring对JPA操作的封装(entiyManager)- 使用
Spring Data JPA
有两个条件:- Spring整合JPA
- 需要有一个JPA的实现框架
运行过程与原理剖析
Spring Data JPA 封装了 JPA 规范操作逻辑,Hibernate实现了JPA规范,并且封装了jdbc操作逻辑,可以进行数据库CRUD。我们使用Spring Data JPA接口就能更加方便的操作数据库。
1. 我们需要做的是创建一个接口:UserDao(extends JpaRepository<User, Long>, JpaSpecificationExecutor<User>)
2. Spring 通过 JdkDynamicAopProxy 的 invoke 方法创建了一个动态代理:SimpleJpaRepository
3. SimpleJpaRepository封装了JPA的操作(借助JPA的api完成数据库的crud)
4. 通过Hibernate完成数据库操作(封装了jdbc操作)
配置文件(application.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">
<!--加载配置文件与要扫描的包-->
<context:property-placeholder location="classpath:properties"></context:property-placeholder>
<context:component-scan base-package="jpa"></context:component-scan>
<!--数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbcUrl}"></property>
<property name="driverClass" value="${driverClass}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
</bean>
<!-- 创建entityManagerFactory对象交给spring容器管理 -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--数据源-->
<property name="dataSource" ref="dataSource"></property>
<!--配置实体类扫描的包-->
<property name="packagesToScan" value="jpa.entity"></property>
<!--配置jpa实现-->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"></bean>
</property>
<!--jpa的供应商适配器 -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--配置是否自动创建数据库表-->
<property name="generateDdl" value="true"></property>
<!--数据库方言:支持的特有语法 -->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"></property>
<property name="database" value="MYSQL"></property>
<property name="showSql" value="true"></property>
</bean>
</property>
<!--jpa方言-->
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean>
</property>
<!--注入jpa的配置信息
加载jpa的基本配置信息和jpa实现方式(hibernate)的配置信息
hibernate.hbm2ddl.auto : 自动创建数据库表
create : 每次都会重新创建数据库表
update : 有表不会重新创建,没有表会重新创建表
-->
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!--事务管理器-->
<bean id="tm" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<!--声明式事务-->
<tx:annotation-driven transaction-manager="tm"></tx:annotation-driven>
<!--整合spring data jpa-->
<jpa:repositories base-package="jpa.dao"
transaction-manager-ref="tm"
entity-manager-factory-ref="entityManagerFactory">
</jpa:repositories>
</beans>
基本查询
UserDao extends JpaRepository<User, Long>
/**
* 基本CRUD操作
* save(user) : 保存或者更新
* 根据传递的对象是否存在主键id,
* 如果没有id主键属性:保存
* 存在id主键属性,根据id查询数据,更新数据
*
* delete(id)
* 根据id删除:调用delete(id)方法
*
* findOne(id)
* 根据id查询:调用findOne(id)方法
*
* getOne(id)
* 根据id查询:调用getOne(id)方法 ,延迟加载(需要@Transactional)
*
* count()
* 查询全部客户数量
*/
复杂查询
/**
* 符合SpringDataJpa的dao层接口规范:
* JpaRepository<操作的实体类类型,实体类中主键属性的类型>
* 封装了基本CRUD操作
* JpaSpecificationExecutor<操作的实体类类型>
* 封装了复杂查询(分页)
*/
public interface UserDao extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
/**
* 使用JPQL的方式查询
*/
@Query(value = "from User where userId = ? ")
public User findById(long id);
/**
* 通过使用 @Query 来执行一个更新操作,
* 为此,我们需要在使用 @Query 的同时,
* 用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询
*/
@Query(value = "update User set userName = ?2 where userId = ?1 ")
@Modifying
public void update(long id,String name);
/**
* sql语句查询
*/
@Query(value = "select * from user",nativeQuery = true)
public List<User> findAll();
/**
* 方法名查询:findBy + 对象中的属性名称(首字母大写)
*/
User findByUserName(String userName);
/**
* 方法名模糊查询查询:findBy + 对象中的属性名称 + Like(传递参数:"ld%")
*/
List<User> findByUserNameLike(String userName);
/**
* 多条件连接(And|Or)查询:findBy + 对象中的属性名称 + Like + And + 对象中的属性名称
*/
List<User> findByUserNameLikeAndUserAge(String a,Integer b );
}
动态查询
Specification:动态查询
JpaSpecificationExecutor 接口的方法:
/**
* 根据条件查询一个对象
* T findOne(Specification<T> spec);
* 根据条件查询集合
* List<T> findAll(Specification<T> spec);
* 根据条件分页查询
* Page<T> findAll(Specification<T> spec, Pageable pageable);
* 排序查询查询
* List<T> findAll(Specification<T> spec, Sort sort);
* 统计查询
* long count(Specification<T> spec);
*
* Root接口 ,代表查询的根对象,可以通过root获取实体类中的属性
* query :代表一个顶层查询对象,用来自定义查询
* cb :用来构建查询,此对象里有很多条件方法
*
* public Predicate toPredicate(Root<T> root,CriteriaQuery<?> query,CriteriaBuilder cb);
*/
public class SpecificationJpaTest {
@Autowired
private UserDao dao;
public void findOne(){
Specification<User> cu = new Specification<>() {
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
/**
* 获取比较的属性:
* 1.Path<Object> userName = root.get("userName");
* 2.构造查询条件 :
* select * from user where user_name = '传智播客';
*
* 第一个参数:需要比较的属性(path对象)
* 第二个参数:当前需要比较的取值
*
*/
return cb.equal(root.<String>get("userName"),"传智播客");
}
};
User one = dao.findOne(cu);
}
public void findByPage(){
PageRequest pageRequest = new PageRequest(0, 2);
Specification<User> cu = new Specification<User>() {
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
/**
* 构造查询
*
* 1.构造名称的精准匹配查询(第一个参数,path(属性),第二个参数,属性的取值)
* Predicate p1 = cb.equal(userName, "传智播客");
*
* 2.构造年龄的精准匹配查询
* Predicate p2 = cb.equal(userAge, 12);
*
* 3.将多个查询条件组合到一起:组合(满足条件一并且满足条件二:与关系cb.and(),满足条件一或满足条件二即可:或关系cb.or())
* Predicate predicate = cb.and(p1, p2);
* return predicate;
*/
return cb.like(root.<String>get("userName"),"传智%");
}
};
Page<User> one =dao.findAll(cu,pageRequest);
}
/**
* 添加排序:
* 创建排序对象,需要调用构造方法实例化sort对象
* 第一个参数:排序的顺序(倒序,正序)
* Sort.Direction.DESC :倒序
* Sort.Direction.ASC :升序
*
* 第二个参数:排序的属性名称
* Sort sort = new Sort(Sort.Direction.DESC,"userId");
* List<User> users = dao.findAll(spec, sort);
*/
}
对象导航查询(Spring Data JPA )
查询一个对象的同时,通过此对象查询它的关联对象(默认懒加载)(需要在实体对象中配置外键(多表关系)@OneToMany)
Boss boss = bossDao.getOne(1l);
Set<Customer> customers = boss.getCustomers();