一、JPA 介绍
JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 一句话,JPA 只是一种接口、一种规范、比如Hibernate、OpenJPA 这些就属于Jpa的具体实现。
二、Spring Data JPA 解决的问题
Spring 为什么要弄出来这个Spring Data JPA 呢? 可以看看以下的代码,然后思考这些代码有什么弊端?
- Dao层接口的定义
public interface UserDao{
void save(User user);
void delete(User user);
...
}
- Dao具体的实现类
public class UserDaoImpl extends HibernateDaoSupport implements UserDao{
public void save(User user){
getHibernateTemplate().save(user);
}
public void delete(User user){
getHibernateTemplate().delete(user);
}
...
}
咋一看上,可能会说,没啥问题呀~多简单明了,别说我这种小学三年级的能看得懂,就算是小学四年级也能看得懂。 诚如这些想法一样,这样的代码已经很精简了,就一句话而已, 难道我们的dao的实现类能再精简么,变成什么都不写么? 这个想法确实还真被spring给想到了。
- 问题所在
上述代码有两个问题:
1. 仔细观察两个具体实现方法, 不管以后是保存用户还是保存商品、或者是保存订单,都是一个模子刻出来的,唯一的不同只是具体操作的数据不同而已,当然删除也是一样。
2. 我们现在使用的是Hibernate 这一种ORM技术,如果以后我们想换成别的ORM 技术,那么需要修改代码。这就违反了`开-闭原则` 【对扩展开发、对修改关闭】 ,要不就是重新打造新的实现类,抛弃旧的实现类。
那么有没有一种方案,让我们能够以不变应万变 ,轻而易举解决掉上面的两个问题? Spring Data JPA是一个完美的解决方案。
在JavaEE 5.0发布的时候,sun公司就提出了jpa的规范,希望整合ORM技术,实现天下归一 。 虽然我们学过的orm技术只有hibernate、但是其实orm技术还有其他的一些方案,比如Apache的 openJPA。 Spring 作为业务逻辑层框架,起到承上启下的作用。所以它对JPA的这些技术实现做了一套封装。只要按照规范来配置即可,甚至你们的dao层实现都不用实现了,它在内部给你实现,如果我们想换到其他的jpa实现方案,那么只需要修改配置即可。
三、Spring Data JPA 下载
- 官网地址
https://projects.spring.io/sp...
- 包含的模块
Spring-data-commons
、 Spring-data-jpa
下面摘录官网对这两个模块的介绍
- Spring Data Commons - Core Spring concepts underpinning every Spring Data project.
这个模块是所有Spring Data 系列工程的核心模块。
- Spring Data JPA - Makes it easy to implement JPA-based repositories.
这个模块是专注于JPA这种操作的模块
Spring Data 还针对其他的不同类型技术提供支持比如:
Spring Data Redis
、Spring Data JDBC
、Spring Data MongoDB
四、Spring Data JPA 入门
该小节,我们需要使用Spring 来配置 data jpa ,并且完成一个简单的查询所有操作。
1. 导入jar包
spring的jar包
core | context | beans | expression 4个核心
aop相关jar包 4个
spring-orm-xxx.jar 、spring-jdbc-xxx.jar 、 spring-tx-xxx.jar 、spring-test-xxx.jar
日志输出jar 4个
hibernate的jar包
核心必须包
其实上面这些也不用看了,就和我们以前整合Spring + Hibernate一样。但是如果要想使用Spring Data JPA 我们需要额外导入以下的jar包
spring-data-commons.jar 、 spring-data-jpa.jar 、 hibernate-entitymanager-5.0.7.Final.jar
2. Dao层代码
这个CrudRepository 是spring已经针对JPA 定义好的一套增删改查的接口 , 我们继承这个接口即可。 实现类,我们也不用自己做了,后面可以让spring data jpa 帮忙创建这个接口的实现类
//继承接口,需要给定两个泛型,第一个User泛型是表示我们要查询的是哪一个表,这里写它对应的持久化类
//第二个泛型,写的是持久化类的主键类型 OID类型。
public interface UserDao extends CrudRepository<User, Integer> {
}
3. Service层代码
public interface UserService {
List<User> findAll();
}
-------------------------------------------------
@Service
@Transactional // 其实这是查询的操作,开不开事务都可以运行。
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public List<User> findAll() {
return (List<User>) userDao.findAll();
}
}
4. 测试类代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
@Autowired
private UserService userService;
@Test
public void testFindAll(){
System.out.println(userService.findAll());
}
}
5. 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:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 导入jdbc.properties文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 使用c3p0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
</bean>
<context:component-scan base-package="com.unknow"/>
<!-- 这里开始配置spring data jpa -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!-- 1. 设置如何连接数据库 -->
<property name="dataSource" ref="dataSource"/>
<!-- 2.可选的连接属性 -->
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<!-- 3. 设置采用什么技术 指定适配器,其实如果我们想换成别的jpa实现技术,只需要替换这个位置即可-->
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
<!-- 4. 指定映射文件 -->
<property name="packagesToScan" value="com.unknow.bean"></property>
</bean>
<!-- 5. 指定我们的dao层在哪个位置,以便data jpa 创建这些接口的动态代理。 -->
<jpa:repositories base-package="com.unknow.dao"/>
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
<!-- 6. 开启事务 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!--如果只是查询的操作,这句话可以不用,但是上面的事务管理员必须声明,否则会报错。因为spring data里面会来检测是否有
声明事务管理员,也不知道它用还是没用,总之它还是找了。 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
五、Spring Data JPA 接口介绍
经过上面的入门例子,也看到了,spring data对于我们编程确实方便了许多。我们无需关心dao层的实现,只需要简单的照着规则去写代码即可,spring data jpa 最重要的就是我们的dao层继承的接口。接下来着重说明它的几个接口。
1. Repository
这个接口是所有接口的父接口,也就是它就是老大了。所有我们后面用的接口都是从这位兄弟身上扩展来的。 下图是Repository 这个接口的继承体系,图中的UserDao 和 MyRepository 是我自己写的,不用理会。 不过要声明的是: 这个Repository 是一个空接口!!!,里面没有任何方法。
package org.springframework.data.repository;
import java.io.Serializable;
/**
* Central repository marker interface. Captures the domain type to manage as well as the domain type's id type. General
* purpose is to hold type information as well as being able to discover interfaces that extend this one during
* classpath scanning for easy Spring bean creation.
* <p>
* Domain repositories extending this interface can selectively expose CRUD methods by simply declaring methods of the
* same signature as those declared in {@link CrudRepository}.
*
* @see CrudRepository
* @param <T> the domain type the repository manages
* @param <ID> the type of the id of the entity the repository manages
* @author Oliver Gierke
*/
public interface Repository<T, ID extends Serializable> {
}
这个repository虽然是个空接口,我们自己来继承它,虽然没有任何要实现的方法,但是我们可以自己写,自己声明方法,这也是允许D,话不多说,走起~
- 自定义接口 MyRepository
public interface MyRepository extends Repository<User, Integer> {
/**
* 根据用户id查询用户
* @param uid
* @return
*/
User findByUid(int uid);
}
- service层代码
public interface UserService {
User findByUid(int uid);
}
------------------------------
@Service
@Transactional //现在仍然是查询这个注解可以不写,为了免得记太多,我就索性都打开了。
public class UserServiceImpl implements UserService {
@Autowired
private MyRepository repository;
@Override
public User findByUid(int uid) {
return repository.findByUid(uid);
}
}
- 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
@Autowired
private UserService userService;
@Test
public void testFindByUid(){
System.out.println(userService.findByUid(1));
}
}
- 结果:
- 疑问:
写到这,有的绝对会有疑问, 这都可以,那这也行的话,我写一个叫做fUid()
或者叫做zhaoUserByUid()
这样的方法名,行不行呢? 答案是: NO!因为我们声明的是接口,具体的实现类还是由人家的spring做出来的。spring胃口比较挑剔,而且为了满足大众化的口味,就做出了一些命名上的要求,要慢慢习惯这种要求,这也正是它的另一个框架Spring Boot的口号
习惯优于配置
。 下图摘自 Spring Data JPA 文档对方法关键字的描述。如:我们想按照用户的
name
查询, 那么可以写成findByName
以此类推
如果觉得这些关键字记不住,那么spring可以允许我们程序员自己定义自己的方法名。
2. 自定义方法名
这个小节主要讲述的是,我们可以自己定义自己的方法名,你爱叫啥叫啥,随意。
- Dao接口
public interface MyRepository extends Repository<User, Integer> {
@Query("from User where uid = ?1") //?1 表示取第一个参数uid 来替代这个?。
User selectUser(int uid);
}
- Service
public interface UserService {
User selectUser(int uid);
}
- 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
@Autowired
private UserService userService;
@Test
public void testSelectUser(){
System.out.println(userService.selectUser(1));
}
}
虽然我们可以自己定义方法名,但是这种做法稍微有点麻烦,所以建议少用这种写法。 只有在以后执行多表查询的时候,我们才需要这么做。
3. CrudRepository
这个接口是扩展了repository, 在这个接口里面默认给大家声明好了一些增删改查的方法,此处给大家演练一个查询所有用户的操作
- Dao
public interface UserDao extends CrudRepository<User, Integer> {
//查询所有的用户
}
- Service
@Service
//@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public List<User> findAll() {
return (List<User>) userDao.findAll();
}
}
- 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
@Autowired
private UserService userService;
@Test
public void testFindAll(){
List<User> list= userService.findAll();
System.out.println("list="+list);
}
}
4.PagingAndSortingRepository
这个接口是是CrudRepository的扩展接口,除了兼备增删改查之外,它还扩展了分页查询&排序的效果。以下是这个接口的代码声明
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
/**
* Returns all entities sorted by the given options.
*
* @param sort
* @return all entities sorted by the given options
*/
Iterable<T> findAll(Sort sort);
/**
* Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
*
* @param pageable
* @return a page of entities
*/
Page<T> findAll(Pageable pageable);
}
- Dao
public interface UserDao extends PagingAndSortingRepository<User, Integer> {
//查询所有的用户
}
- Service
public interface UserService {
/**
* 返回值是page 这个类型是springdatajpa定义好的。其实就和
* 我们自己平常写的PageBean 一样。
* @param pageable
* @return
*/
Page<User> findByPage(Pageable pageable);
}
---------------------------------------------------------
@Service
//@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public Page<User> findByPage(Pageable pageable) {
return userDao.findAll(pageable);
}
}
- 测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJpa {
@Autowired
private UserService userService;
@Test
public void testFindByPage(){
//这里ctrl + T 看实现类,然后找PageRequest
// page : page表示请求的页码数, 从0开始,0就代表第一页。
//size : 表示每页拿多少条记录
Pageable pageable = new PageRequest(1, 2);
Page<User> page = userService.findByPage(pageable);
//当前是第几页 这个页码从0开始的,大家可以加上1表示从1开始。
System.out.println(page.getNumber());
System.out.println(page.getTotalPages()); //总页数
System.out.println(page.getSize()); //每页个数
System.out.println(page.getTotalElements()); //总记录数
System.out.println(page.getContent()); //总页数
}
}
六、
- JpaReposityry&多表查询
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。