Spring Data Jpa的详细介绍
Spring Data Jpa的详细介绍
一、Jpa是什么
JPA(Java Persistence API)
意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化规范,JPA的出现主要是为了简化持久层开发以及整合ORM技术,结束Hibernate、TopLink、JDO等ORM框架各自为营的局面。JPA是在吸收现有ORM框架的基础上发展而来,易于使用,伸缩性强。
总的来说,JPA包括以下3方面的技术:
ORM映射:支持XML和注解描述对象和表之间的映射关系
API:操作实体对象来执行CRUD(create read update delete)(增删改查)操作
查询语言:通过面向对象而非面向数据库的查询语言(JPQL)查询数据
二、Spring Data Jpa 简介
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据的访问和操作。Spring Data JPA不需要过多的关心Dao层的实现,只需关注我们继承的接口,按照一定的规则去编写我们的接口即可,spring会按照规范动态生成我们接口的实现类进行注入,并且实现类里包含了一些常规操作的方法实现。如果使用JPA提供的接口来操作ORM框架,可以不写任何实现就能对数据库进行CRUD操作,还可以进行简单的分页,排序操作。
三、Spring Data Jpa核心接口(API)
Repository:
所有接口的父接口,而且是一个空接口,目的是为了统一所有Repository的类型,让组件扫描的时候能进行识别。
CrudRepository:
是Repository的子接口,提供CRUD(增删改查)的功能。
PagingAndSortingRepository:
是CrudRepository的子接口,添加分页和排序的功能。
JpaRepository:
是PagingAndSortingRepository的子接口,增加了一些实用的功能,例如批量操作。
JpaSpecificationExecutor:用来做负责动态条件查询的接口。
Specification:是Spring Data JPA提供的一个查询规范,要做复杂的查询,只需围绕这个规范来设置查询条件即可。
四、Spring Data Jpa的使用
**1.在项目的pom.xml中添加spring-data-jpa的依赖配置
<!-- c3p0数据库连接池 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<!-- spring-data-jpa -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.9.0.RELEASE</version>
</dependency>
2.在spring核心配置文件中配置spring-data-jpa相关项
applicationContext.xml
与hibernate Mybatis配置一样
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!-- 1、创建数据源 -->
<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/1905a?useUnicode=true&characterEncoding=UTF-8" />
<property name="user" value="root" />
<property name="password" value="root" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource" />
<!-- 扫描实体类的包 -->
<property name="packagesToScan" value="com.fh.model" />
<!-- 指定jpa的提供商 -->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
</property>
<!--JPA的供应商适配器 -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!-- true: HBM2DDL_AUTO为"update" ,有表就维护表,没有表就创建表 false: 默认为false -->
<property name="generateDdl" value="true" />
<property name="database" value="MYSQL" />
<!-- 数据库方言:根据这个方言生成不同数据库的sql -->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
<property name="showSql" value="true" />
</bean>
</property>
<!-- 可选:Jpa的方言 -->
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
<!-- 3、配置事务 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- 事务通知和aop切面这里就不配置: 今天是dao层的代码 -->
<!-- 4、 配置SpringDataJpa的相关配置 base-package:指定基包 entity-manager-factory-ref:
引用实体类管理器工厂 transaction-manager-ref: 引用平台事务管理器 -->
<jpa:repositories base-package="com.fh.dao"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager" />
<!-- 5、开启组件的扫描 -->
<context:component-scan base-package="com.fh" />
<!-- 定义advice,配置传播特性、事务隔离级别、只读事务、回滚策略 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
<tx:method name="update*" propagation="REQUIRED"
rollback-for="java.lang.Exception" />
<tx:method name="delete*" propagation="REQUIRED"
rollback-for="java.lang.Exception" />
<tx:method name="save*" propagation="REQUIRED"
rollback-for="java.lang.Exception" />
<tx:method name="*" propagation="REQUIRED" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 切点配置 execution(* com.fh.service.impl.*.*(..)) 第一个*:任意返回值 第二个*:包下任意的类
第三个*:类中的所有方法 (..):任意参数 -->
<aop:config>
<aop:pointcut expression="execution(* com.fh.service.impl.*.*(..))"
id="servicePointcut" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="servicePointcut" />
</aop:config>
</beans>
3.编写实体类并使用JPA注解配置实体类和数据库表的映射关系
JPA不需要自己创建数据库,只需要在实体类中通过注解配置好,就会自动创建出所需要的数据
注意:日期这块要特别注意
User.class
//name="t_user" 数据库的表名
@Table(name="t_user")
//@Entity表明是一个实体类
@Entity
public class User {
@Id
//指名这是一个主键,auto会自动根据方言来生成主键,IDENTITY mysql的主键生成
@GeneratedValue(strategy = GenerationType.AUTO)
//@Column 字段名,类型会自动根据属性类型匹配,如果不写,默认就是属性名
@Column(name="user_id")
private Integer userId;
//定义字段名
@Column(name="user_name")
private String userName;
@Column(name="real_name")
private String realName;//真实姓名
@Column(name="user_sex")
private Integer userSex;//性别
//@DateTimeFormat是日期的展示格式
//加上后,格式为:2020-01-01
//不加: 默认格式为:2020-01-01 12:03:22
@DateTimeFormat(pattern = "yyyy-MM-dd")
//@Temporal设置数据库的字段类型 ,
///加上为:date类型(2020-01-01)
//不加默认:datetime(2020-01-01 12:03:22)
//与@DateTimeFormat一定要对应好
@Temporal(TemporalType.DATE)
@Column(name="user_birthday")
//json数据的展示
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private Date userBirthday;//生日
//创建日期有时分秒
//不能省略
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name="create_date")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date createDate;//创建时间
//不能省略
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Column(name="update_Date")
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private Date updateDate;//修改时间
//生成getter setter方法
}
建立包结构
service serviceImpl
Repository 替代了dao层
通过JPA进行增删改查
这里通过junit进行测试,所以需要导入相关的jar包
<!-- java单元测试框架 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
Repository.java
/**
* Repository替代了dao类
* 需要继承extends JpaRepository<User, Integer> Integer 是id的类型
* 只要继承该类,就会自动有增删改查 的方法,直接可以调用
* @author srj
*
*继承JpaRepository(增删改查),
*第一个泛型:我们要操作的实体类,
*第二个泛型:主键属性的数据类型
*/
public interface UserRepository extends JpaRepository<User, Integer>{
//只要继承JpaRepository,默认会有增删改查的方法,直接调用就可以
}
测试类
UserRepositoryTest.java
//用于指定junit运行环境,是junit提供给其他框架测试环境接口扩展,为了便于使用spring的依赖注入
//spring提供了org.springframework.test.context.junit4.SpringJUnit4ClassRunner作为Junit测试环境
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
}
准备完成:
增加:
UserRepositoryTest.java
/**
* 新增
*/
@Test
public void addUser() {
User user = new User();
user.setUserName("李四111");
user.setUserSex(2);
user.setUserBirthday(new Date());
//直接调用新增方法即可
userRepository.saveAndFlush(user);
}
删除
/**
* 删除
*/
@Test
public void deleteUser() {
//直接调用即可
userRepository.delete(2);
}
修改
/**
* 修改
*/
@Test
public void updateUser() {
User user = userRepository.findOne(1);
user.setUserName("小红");
user.setUserEmail("[email protected]");
user.setCreateDate(new Date());
userRepository.saveAndFlush(user);
}
查询
/**
* 查询
*/
@Test
public void queryUser() {
List<User> userList = userRepository.findAll();
//循环输出所有值
for (Iterator iterator = userList.iterator(); iterator.hasNext();) {
User user = (User) iterator.next();
System.out.println(user.getUserName());
}
}
如果要进行特殊查询等,需要继承接口JpaSpecificationExecutor,根据规则自定方法
//继承JpaSpecificationExecutor(复杂查询,动态查询),泛型: 就是我们操作的实体类
五、Spring Data Jpa查询方法命名规则
Spring Data JPA为自定义查询提供了一些表达条件查询的关键字,大致如下:
And:等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd);
Or :等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr);
Between :等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min);
LessThan:等价于 SQL 中的 “<”,比如 findBySalaryLessThan(int max);
GreaterThan:等价于 SQL 中的">",比如 findBySalaryGreaterThan(int min);
IsNull :等价于 SQL 中的 “is null”,比如 findByUsernameIsNull();
IsNotNull:等价于 SQL 中的 “is not null”,比如 findByUsernameIsNotNull();
NotNull:与 IsNotNull 等价;
Like :等价于 SQL 中的 “like”,比如 findByUsernameLike(String user);
NotLike :等价于 SQL 中的 “not like”,比如 findByUsernameNotLike(String user);
OrderBy:等价于 SQL 中的 “order by”,比如 findByUsernameOrderBySalaryAsc(String user);
Not:等价于 SQL 中的 “!=”,比如 findByUsernameNot(String user);
In :等价于 SQL 中的 “in”,比如 findByUsernameIn(Collection userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
NotIn:等价于 SQL 中的 “not in”,比如 findByUsernameNotIn(Collection userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
public interface UserDao extends JpaRepository<User, Integer>,JpaSpecificationExecutor<User>{
}
And使用
//and
User findByUserNameAndUserSex(String userName,Integer userSex);
/**
* 通过userName和userSex查询
*/
@Test
public void findByUsernameAndUserSexTest() {
User user = userRepository.findByUserNameAndUserSex("李四", 10);
System.out.println(user);
}
between使用
//between
List<User> findByUserSexBetween(int maxSex, int minSex);
/**
* between
*/
@Test
public void findByUserSexBetweenTest() {
List<User> userlist = userRepository.findByUserSexBetween(0, 4);
//循环输出所有值
for (Iterator iterator = userlist.iterator(); iterator.hasNext();) {
User user = (User) iterator.next();
System.out.println(user.getUserName());
}
}
like使用
//like
List<User> findByUserNameLike(String user);
/**
* like
*/
@Test
public void findByUserNameLikeTest() {
//查询姓李的人
List<User> userlist = userRepository.findByUserNameLike("李%");
//循环输出所有值
for (Iterator iterator = userlist.iterator(); iterator.hasNext();) {
User user = (User) iterator.next();
System.out.println(user.getUserName());
}
}
OrderBy使用
//OrderBy:等价于 SQL 中的 "order by",比如
List<User> findByUserNameLikeOrderByUserIdAsc(String user);
/**
* 条件查询+排序
*/
@Test
public void findByUserNameOrderByUserSexAscTest() {
//查询姓李的人
//List<User> userlist = userRepository.findByUserNameOrderByUserIdAsc("");
List<User> userlist = userRepository.findByUserNameLikeOrderByUserIdAsc("李%");
//循环输出所有值
for (Iterator iterator = userlist.iterator(); iterator.hasNext();) {
User user = (User) iterator.next();
System.out.println("name======"+user.getUserName()+"======sex=====");
}
}
JPA分页使用
一.分页
UserService .java
public interface UserService {
//分页
public List<User> queryPage(Integer pageIndex,Integer pagesize);
}
UserServiceImpl .java
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserRepository userRepository;
/**
* 分页
*/
public List<User> queryPage(Integer pageIndex,Integer pagesize){
//构造分页对象
PageRequest pageRequest = new PageRequest(pageIndex, pagesize);
//开始分页查询获取page对象
Page<User> page = userRepository.findAll(pageRequest);
//查询
List<User> userList = page.getContent();
return userList;
}
}
UserServiceImplTest.java
//用于指定junit运行环境,是junit提供给其他框架测试环境接口扩展,为了便于使用spring的依赖注入
//spring提供了org.springframework.test.context.junit4.SpringJUnit4ClassRunner作为Junit测试环境
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class UserServiceImplTest {
@Autowired
private UserService userService;
/**
* 测试分页
*/
@Test
public void queryPageTest() {
List<User> userList = userService.queryPage(0,2);
for (User user : userList) {
System.out.println(user);
System.out.println(user.getUserName());
}
}
}
二.分页+排序 先排序再分页
UserService .java
//分页+排序
public List<User> queryPageAndSorting(Integer pageIndex,Integer pagesize,String name,Direction orderBy);
UserServiceImpl .java
/**
* 先排序再分页
* 分页+排序
* @param pageIndex 当前第几页 从0开始
* @param pagesize 每页条数
* @param name 按照什么属性【字段名】分页
* @param orderBy 排序规则:asc desc
* @return
*/
public List<User> queryPageAndSorting(Integer pageIndex,Integer pagesize,String name,Direction orderBy){
//排序
Sort sort = new Sort(orderBy,name);
//构建分页对象
PageRequest pageRequest = new PageRequest(pageIndex, pagesize,sort);
//开始分页查询,获取page对象
Page<User> page = userRepository.findAll(pageRequest);
//获取分页的list集合
List<User> userList = page.getContent();
return userList;
}
UserServiceImplTest.java
/**
* 测试分页+排序
*/
@Test
public void queryPageAndSortingTest() {
List<User> userList = userService.queryPageAndSorting(0,2,"userId",Direction.DESC);
for (User user : userList) {
System.out.println(user);
}
}
三.先分页再排序
UserService .java
//分页+排序
public List<User> querySortingAndPage(Integer pageIndex,Integer pagesize,String name,Direction orderBy);
UserServiceImpl .java
/**
* 先分页再排序
* @param pageIndex
* @param pagesize
* @param name
* @param orderBy
* @return
*/
public List<User> querySortingAndPage(Integer pageIndex,Integer pagesize,String name,Direction orderBy){
PageRequest pageRequest = new PageRequest(pageIndex, pagesize);
//开始分页查询,获取page对象
Page<User> page = userRepository.findAll(pageRequest);
Sort sort = new Sort(orderBy,name);
//Sort sort2 = page.getSort();
//获取分页的list集合
List<User> userList = page.getContent();
//排序
PageRequest pageRequest1 = new PageRequest(pageIndex, pagesize,sort);
//开始分页查询,获取page对象
Page<User> page1 = userRepository.findAll(pageRequest);
//获取分页的list集合
List<User> userList1 = page.getContent();
return userList1;
}
UserServiceImplTest.java
@Test
public void querySortingAndPage() {
List<User> userList = userService.querySortingAndPage(0,2,"userId",Direction.DESC);
for (User user : userList) {
System.out.println(user);
}
}