简介
SpringData JPA是 JPA的一种实现,极大的简化了JPA的开发,原始JPA的开发,需要创建实体管理工厂,使用实体管理器定义各种查询进行CRUD操作,而SpringData JPA只需要通过核心接口Repository和它的子类就能很方便的操作数据库。
Repository
1. Repository:最顶层的接口,一个空的接口,统一所有的Repository类型,并且能够让组件扫描的时候能够自动识别
2. CrudRepository: Repository的子接口,提供CRUD的操作
3. PagingAndSortingRepository: CrudRepository的子接口,添加了分页和排序的功能
4. JpaRepository: 是PagingAndSortingRepository的子接口,增加一些实用的功能,比如批量操作
5. JpaSpecificationExecutor:来定义复杂查询
使用SpringData JPA
将SpringDataJPA整合Spring,将EntityManagerFactory的创建交给Spring容器,只需要在xml中配置JPA的Repository的接口位置,就可以很方便的获取到Repository的bean,使用时候直接在service层注入定义的repository.
配置文件:
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="cn.bing"/>
<!-- 配置数据库资源文件的位置-->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- jdbc.properties 中的key必须定义为 jdbc.username,格式开头的 -->
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driverClassName}"/>
</bean>
<!-- 2. 配置 JPA 的 EntityManagerFactory -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 配置jpa的适配器 -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
<!-- 配置实体所在的包 -->
<property name="packagesToScan" value="cn.bing.pojo"/>
<!-- 配置jpa的属性 -->
<property name="jpaProperties">
<props>
<!-- 二级缓存相关 -->
<!--
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="net.sf.ehcache.configurationResourceName">ehcache-hibernate.xml</prop>
-->
<!-- 生成的数据表的列的映射策略 -->
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<!-- hibernate 基本属性 -->
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- 3. 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- 4. 配置支持注解的事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 5. 配置 SpringData -->
<!-- 加入 jpa 的命名空间 -->
<!-- base-package: 扫描 Repository Bean 所在的 package -->
<jpa:repositories base-package="cn.bing.dao"
entity-manager-factory-ref="entityManagerFactory"/>
</beans>
1 . 引入Jar
2. 创建实体
@Entity表示这个类是一个实体类,参与JPA和数据库表的映射
@Table表示具体映射的表名
@Id表示这个字段是主键,@GeneratedValue表示这个主键的生成策略
@Column表示映射到数据库的表的字段名,字段名和表字段不一致,修改注解@Column的name属性
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Column
private Integer age;
@Column
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3. 编写接口继承Repository的子类,并且需要在spring的配置文件配置
<!-- 5. 配置 SpringData -->
<!-- 加入 jpa 的命名空间 -->
<!-- base-package: 扫描 Repository Bean 所在的 package -->
<jpa:repositories base-package="cn.bing.dao"
entity-manager-factory-ref="entityManagerFactory"/>
一般都是选择继承JpaRepository方便直接调用现有的方法进行CRUD,继承JpaSpecificationExecutor则是方便定义一些复杂的查询。
JpaRespository<T,D> T 表示实体,D表示实体的主键ID类型
public interface UserRepository extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
}
自定义接口继承父类的Repository后,自动获取的操作数据的API,只能进行简单的操作,需要自定义查询。
repository.findOne(1);//根据id查询
repository.findAll();//查询
repository.delete(new User());//delete操作
repository.saveAndFlush(new User());//insert操作
关于SpringData JPA查询的定义
1. spring data 对于定义方法的查询策略
查询策略是spring data 根据方法名称取解析用户的查询意图,第一种,根据方法的命名规则解析,第二种是通过Query去解析,如果两种同时存在时,springdata按照那种解析方法名,这就是spring data的查询策略,查询策略可以在<jpa:repositorys/> 属性query-lookup-strategy 配置
CREATE: 通过解析方法的名称来创建查询,也就是下面的规则1
USE_DECLARED_QUERY:根据定义好的语句去查询,如果找不到,抛出异常信息。查询语句定义在某个注解或者方法上。
CREATE_IF_NOT_FOUND:优先查询方法上是否有定义好的查询语句,如果没有,则按照方法名称解析,这是默认的策略。
public interface UserRepository extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
//根据springData的关键字命名方法名称,可以不用写实现类
List<User> findByNameAndAge(String name, Integer age);
List<User> findByNameLikeAndAge(String name, Integer age);
//@Query注解里面写JPQL语句,定义查询
@Query(nativeQuery = false,value = " SELECT p FROM User p WHERE id = ?1")
User readId(Integer id);
//Query注解也可以定义SQL语句,只要将nativeQuery属性改为true
@Query(nativeQuery = true, value = "select name from user where id = :id")
String findNamebyId(@Param("id")Integer id);
//@Modifying修饰方法,告知SpringData,这是一个UPATE或者DELETE操作
//在service层的方法上添加事务操作
@Modifying
@Query(nativeQuery = true,value = "update user set name = ?1 where id = ?2 ")
int updateUserNameById(String name,Integer id);
}
规则1:根据SpringData JPA的关键字定义方法名,方法的参数顺序和关键字的顺序一致,名称可以不一致。
Spring Data JPA对方法名称进行解析的时候,会将一些无用的前缀名自动去除,比如find、findBy、read、readBy,然后根据关键字解析成对应JPQL语句。也要注意方法的参数顺序和定义的关键字的顺序一致。
规则2:定义方法时候,上面加上注解@Query,默认nativeQuery是false,此时value填入的是JPQL语句,修改nativeQuery是true,就能够写入SQL语句
//@Query注解里面写JPQL语句,定义查询
@Query(nativeQuery = false,value = " SELECT p FROM User p WHERE id = ?1")
User readId(Integer id);
//Query注解也可以定义SQL语句,只要将nativeQuery属性改为true
@Query(nativeQuery = true, value = "select name from user where id = :id")
String findNamebyId(@Param("id")Integer id);
注意参数注入的方式有两种:
1. ?下标,下标从1开始
2. :xxx ...:yyy,xxx和yyy必须是实体的属性名,并且方法参数上加上对应的注解@Param("xxx")和@Param('yyy')
@Modify和事务
可以通过JPQL语句定义update/delete,此时在@Query注解中定义,必须加上@Modify,告诉spring data 这是一个update/delete操作。
update/delete操作需要事务支持,必须在service层,添加事务,因为spring data,默认情况下每个方法是只读事务,不能完成update/delete操作。
public interface UserRepository extends JpaRepository<User,Integer>, JpaSpecificationExecutor<User> {
//根据springData的关键字命名方法名称,可以不用写实现类
List<User> findByNameAndAge(String name, Integer age);
List<User> findByNameLikeAndAge(String name, Integer age);
//@Query注解里面写JPQL语句,定义查询
@Query(nativeQuery = false,value = " SELECT p FROM User p WHERE id = ?1")
User readId(Integer id);
//Query注解也可以定义SQL语句,只要将nativeQuery属性改为true
@Query(nativeQuery = true, value = "select name from user where id = :id")
String findNamebyId(@Param("id")Integer id);
//@Modifying修饰方法,告知SpringData,这是一个UPATE或者DELETE操作
//在service层的方法上添加事务操作
@Modifying
@Query(nativeQuery = true,value = "update user set name = ?1 where id = ?2 ")
int updateUserNameById(String name,Integer id);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository repository;
//对于SpringData jpa的update或者delete操作,必须在service层使用事务,直接使用仓库的方法会报错
//另外,SpringData jpa 的 JPQL语法不支持insert
@Transactional(propagation = Propagation.REQUIRED)
public int updateUserNameById(String name,Integer id){
return repository.updateUserNameById(name,id);
}
}
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class SpringDataTest {
@Autowired
private UserRepository repository;
@Autowired
private UserService service;
@Test
public void test(){
service.updateUserNameById("张三",1);
}
}
分页和模糊查询
模糊查询
需要注意的是%%外面不需要再加上''
//模糊查询
@Query(nativeQuery = true,value = " select * from user where name like %?1% ")
User findUserByLikeName(String name);
分页对象
1. 要求自定义的Repository必须继承了PagingAndSortingRepository或者他的子类JpaRepository
2. 分页对象是Pageable接口的实现类PageRequest
public class PageRequest implements Pageable, Serializable {
private static final long serialVersionUID = 8280485938848398236L;
private final int page;//页码,从0开始,0表示第一页,1表示第二页,以此类推
private final int size;//每页显示的记录数
private final Sort sort;//排序规则
Sort对象,定义排序规则,常用的是下面这种构造函数,支持可变参数的
public Sort(Sort.Direction direction, String... properties) {
this(direction, (List)(properties == null ? new ArrayList() : Arrays.asList(properties)));
}
定义分页查询,只需要将查询的参数和分页对象作为参数。
Page<User> findByNameLike(String str , Pageable pageable);
@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class SpringDataTest {
@Autowired
private UserRepository repository;
@Test
public void test(){
/**
* SpringData jpa 的分页
* Pageable接口的实现类是PageRequest,Page接口的实现类是PageImpl。
*/
Pageable page = new PageRequest(0,2,new Sort(Sort.Direction.DESC,"id"));
Page<User> personList = repository.findByNameLike("张%",page);
System.out.println("总记录数" + personList.getTotalElements());
System.out.println("当前第几页: " + (personList.getNumber() + 1));
System.out.println("总页数: " + personList.getTotalPages());
System.out.println("当前页面的记录数:" + personList.getContent());
System.out.println("当前页面的记录数: " + personList.getNumberOfElements());
}
}