文档章节

一步步学习 Spring Data 系列之JPA(一)

张廷
 张廷
发布于 2012/07/23 10:08
字数 2489
阅读 13750
收藏 23

大概有半年多没有写博客了,主要是最近忙于工作,也没来得及与大家分享技术。当然现在的技术大多都有人写其博客分享了,也找不到合适写的,所以也就懒得写了。最近在SpringSource上看到了一好玩的东东,于是就照着官方的文档玩了一把,再根据自己的理解写给博友们品鉴,如有不对的地方,欢迎博友们提出,笔者会一一记录下来,以便后续改进。

引入:

Spring Data是SpringSource基金会下的一个用于简化数据库访问,并支持云服务的开源框架。其主要目标是使得数据库的访问变得方便快捷,并支持map-reduce框架和云计算数据服务。对于拥有海量数据的项目,可以用Spring Data来简化项目的开发。

然而针对不同的数据储存访问使用相对的类库来操作访问。Spring Data中已经为我们提供了很多业务中常用的一些接口和实现类来帮我们快速构建项目,比如分页、排序、DAO一些常用的操作。

今天主要是对Spring Data下的JPA模块进行讲解。

为什么说Spring Data能帮助我们快速构建项目呢,因为Spring Data已经在数据库访问层上帮我们实现了公用功能了,而我们只需写一个接口去继承Spring Data提供给我们接口,便可实现对数据库的访问及操作,类似于spring-orm的TemplateDAO。

 

----------------------------------------------邪恶的分割-------------------------------------------------------

核心接口:

public interface Repository<T, ID extends Serializable> {

}


这个接口只是一个空的接口,目的是为了统一所有Repository的类型,其接口类型使用了泛型,泛型参数中T代表实体类型,ID则是实体中id的类型。

再来看一下Repository的直接子接口CrudRepository中的方法:

public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {

	<S extends T> S save(S entity);

	<S extends T> Iterable<S> save(Iterable<S> entities);

	T findOne(ID id);

	boolean exists(ID id);

	Iterable<T> findAll();

	Iterable<T> findAll(Iterable<ID> ids);

	long count();

	void delete(ID id);

	void delete(T entity);

	void delete(Iterable<? extends T> entities);

	void deleteAll();
}

此接口中的方法大多是我们在访问数据库中常用的一些方法,如果我们要写自己的DAO类的时候,只需定义个接口来集成它便可使用了。

再来看看Spring Data未我们提供分页和排序的Repository的接口PagingAndSortingRepository:

public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {

	Iterable<T> findAll(Sort sort);

	Page<T> findAll(Pageable pageable);
}

这些Repository都是spring-data-commons提供给我们的核心接口,spring-data-commons是Spring Data的核心包。这个接口中为我们提供了数据的分页方法,以及排序方法。[b]看吧,spring-data让我们省了很多心了,一切都按照这个规范进行构造,就连业务系统中常用到的一些操作都为我们考虑到了,而我们只需更用心的去关注业务逻辑层。[/b]spring-data将repository的颗粒度划得很细,其实我觉得spring的框架中将每个类的颗粒度都划得很细,这主要也是为了责任分离。

----------------------------------------------邪恶的分割线------------------------------------------------------

JPA实现:

针对spring-data-jpa又提供了一系列repository接口,其中有JpaRepository和JpaSpecificationExecutor,这2个接口又有什么区别呢,我们分别来看看这2个接口的源码。

JpaRepository.class

public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {

	List<T> findAll();

	List<T> findAll(Sort sort);

	<S extends T> List<S> save(Iterable<S> entities);
	void flush();

	T saveAndFlush(T entity);

	void deleteInBatch(Iterable<T> entities);

	void deleteAllInBatch();

这个类继承自PagingAndSortingRepository,看其中的方法,可以看出里面的方法都是一些简单的操作,并未涉及到复杂的逻辑。当你在处理一些简单的数据逻辑时,便可继承此接口,看一个小例子吧。本文JPA供应者选择的是Hibernate EntityManager,当然读者们也可以选择其他的JPA供应者,比如EclipseLink、OpenJPA,反正JPA是个标准,在无须修改的情况下便可移植。

先定义一用户实体类User.class:

@Entity
@Table( name = "spring_data_user" )
@PrimaryKeyJoinColumn( name = "id" )
public class User extends IdGenerator{

	private static final long serialVersionUID = 1L;
	
	private String name;
	private String username;
	private String password;
	private String sex;
	private Date birth;
	private String address;
	private String zip;
        
        //省略getter和setter
}

Id生成策略是采用的表生成策略,这里就不贴代码了,spring的配置文件我也就不贴出来了,反正就那些东西,网上一查,遍地都是。后续我会在将demo附上来。

实体类是有了,现在得写一个持久层,这样才能操作数据库啊,现在我们来看一下持久层。IUserDao.class:

@Repository("userDao")
public interface IUserDao extends JpaRepository<User, Long>{}

再在spring的配置文件中加上以下代码。

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/data/jpa
	http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

	<jpa:repositories base-package="org.tea.springdata.**.dao" />
</beans>

加上这段后Spring就会将指定包中@Repository的类注册为bean,将bean托管给Spring。这样定义完了就OK了!哦,就这样就可以操作数据库了?
是的,前面我就已经说了,Spring data已经帮我们写好一个实现类了,而简单的操作我们只须这样继承JpaRepository就可以做CRUD操作了。再写个业务类来测试一把吧。由于我用的Cglib来动态代理,所以就不定义接口了,直接定义类UserService.class:

 

@Service("userService")
public class UserService {
	
	@Autowired
	private IUserDao dao;
	
	public void save(User user) {
		dao.save(user);
	}

	public void delete(Long id) {
		dao.delete(id);
	}

	public void update(User user) {
		dao.saveAndFlush(user);
	}

	public List<User> findAll() {
		return dao.findAll();
	}
}

来写一单元测试。

 

public class UserServiceTest {
	
	private static ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
	
	private static UserService userService = (UserService) context.getBean("userService");
	
	public void saveUser() {
		StopWatch sw = new StopWatch(getClass().getSimpleName());
		sw.start("Add a user information.");
		User u = new User();
		u.setName("John");
		u.setSex("Man");
		u.setUsername("JohnZhang");
		u.setPassword("123456");
		u.setBirth(new Date());
		userService.save(u);
		sw.stop();
		System.err.println(sw.prettyPrint());
	}

     public static void main(String[] args) {
		UserServiceTest test = new UserServiceTest();
		test.saveUser();
	}
}

绿了,高兴了,测试通过!
额,都没用Junit怎么会绿呢,开个玩笑。
其余继承下来的操作方法,大家都可以自己测试一下,如没意外,应该都会测试通过。

这只是spring data jpa简单的使用,而往往在项目中这一点功能并不能满足我们的需求。这是当然的,在业务中查询是一件非常头疼的事,毕竟不可能只是对一张表的查询是吧? 其实在业务中往往会涉及到多张表的查询,以及查询时需要的各种条件。当然这不用担心,毕竟这是对JPA的支持,而我们在用JPA原生态API的时候往往可能会把一些个方法写得很凌乱,没得一个具体的规范来写自己的方法在后期维护上肯定会很困难。当然你自己也可以封装一些方法来使用,而当我们使用到Spring Data JPA时,它已经帮助我们完成了这个方法的规范了。

来一起看一下复杂查询时它为我们提供的接口。

JpaSpecificationExecutor.class

public interface JpaSpecificationExecutor<T> {

	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);
}

在这个接口里面出现次数最多的类就是Specification.class,而这个类主要也就是围绕Specification来打造的,Specification.class是Spring Data JPA提供的一个查询规范,而你只需围绕这个规范来设置你的查询条件便可,我们来看一下Specification.class这个接口中有些什么东西。

Specification.class

public interface Specification<T> {

	Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}

只有一个方法toPredicate,而其中的参数大家并不陌生,都是JPA规范中的,ROOT查询中的条件表达式、CriteriaQuery条件查询设计器、CriteriaBuilder条件查询构造器,而我们在使用复杂对象查询时,实现该方法用JPA去构造对象查询便可。

下面来看一个小例子:

 

@Repository("userDao")
public interface IUserDao extends JpaSpecificationExecutor<User>{
}

仍然只是一个空接口,这次继承的是JpaSpecificationExecutor了。
再写一测试用例:查询用户表中name包含Sam的记录,并分页按照birth排倒序

 

public class UserDaoTest {

	private static ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

	private static IUserDao userDao = (IUserDao) context.getBean("userDao");

	public void findBySpecAndPaginate() {
		Page<User> page = userDao.findAll(new Specification<User>() {
			@Override
			public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				root = query.from(User.class);
				Path<String> nameExp = root.get("name");
				return cb.like(nameExp, "%Sam%");
			}

		}, new PageRequest(1, 5, new Sort(Direction.DESC, new String[] { "birth" })));

		StringBuilder stout = new StringBuilder(" 以下是姓名包含Sam人员信息 : ").append("\n");
		stout.append("| 序号 | username | password | name | sex | birth |").append("\n");
		int sortIndex = 1;
		for (User u : page.getContent()) {
			stout.append(" | ").append(sortIndex);
			stout.append(" | ").append(u.getUsername());
			stout.append(" | ").append(u.getPassword());
			stout.append(" | ").append(u.getName());
			stout.append(" | ").append(u.getSex());
			stout.append(" | ").append(u.getBirth());
			stout.append(" | \n");
			sortIndex++;
		}
		System.err.println(stout);
	}

	public static void main(String[] args) {
		UserDaoTest test = new UserDaoTest();
		test.findBySpecAndPaginate();
	}
}

当然,这只是一个测试,很简单的一个条件查询方法。你也可以设计复杂的查询来得到自己所需的结果,我这只是写一个很简单的方法来带大家入门。

写了两篇文章了,还没有讲Spring Data JPA为什么只需定义接口就可以使用,其实这也不难发现,查看源码,可以找到针对JpaRepository和JpaSpecificationExecutor有一个实现类,SimpleJpaRepository.class,这个类实现了刚才所提的两个接口。而Spring在给我们注入实现类的时候,就正是这个SimpleJpaRepository.class,具体的实现方式我就不在这意义赘述了,大家如果有兴趣可以去查看它的源码,和传统的JPA实现是一样的。

通过这篇文章我们学习到了,当要使用复杂的条件查询时,我们可以选择使用此接口来完善我们的需求,这篇文章就讲到这里,在下一篇文章中我主要是讲Spring Data JPA为我们提供的注解查询。

 

© 著作权归作者所有

张廷
粉丝 4
博文 1
码字总数 2489
作品 0
綦江
私信 提问
加载中

评论(3)

西夏一品堂
西夏一品堂
select *
from tx_person where city_id=1
order by (case when stype='1' then 1 else 0 end ) desc ,userid desc

这种的排序怎么做
张廷
张廷 博主

引用来自“欧文杰”的评论

如果有多个数据库怎么定义接口呢?

配置多个数据源就行了, 然后在注入dataSource的时候指定需要的数据源就可以了
欧文杰
如果有多个数据库怎么定义接口呢?
解决spring jpa中配置文件报'jpa:repositories'的问题

一、问题描述 使用spring jpa,报no declaration can be found for element 'jpa:repositories'错误的解决方案 Multiple annotations found at this line: - cvc-complex-type.2.4.c: The mat......

cloud-coder
2014/04/17
0
1
Spring Boot [组件学习-Spring Data JPA]

导读: 在上篇文章中对Spring MVC常用的一些注解做了简要的说明,在这篇文章中主要对Spring Data JPA 做一个简要的说明,并附有一个简单的例子,可以体会到Spring Data JPA 的强大之处。 Sp...

yangrd
2018/08/27
0
0
下载最新springside jpa schema问题

xsi:schemaLocation="http://www.springframework.org/schema/data/jpa/spring-jpa-1.1.xsd“ 出现Description Resource Path Location Type Referenced file contains errors (http://www.s......

kennylixi
2012/08/09
578
3
SpringBoot系列教程JPA之delete使用姿势详解

原文: 190702-SpringBoot系列教程JPA之delete使用姿势详解 常见db中的四个操作curd,前面的几篇博文分别介绍了insert,update,接下来我们看下delete的使用姿势,通过JPA可以怎样删除数据 一...

小灰灰Blog
07/04
0
0
(入门帖)使用 Spring Data JPA 简化 JPA 开发

本文主要讲述 Spring Data JPA,但是为了不至于给 JPA 和 Spring 的初学者造成较大的学习曲线,我们首先从 JPA 开始,简单介绍一个 JPA 示例;接着重构该示例,并引入 Spring 框架,这两部分...

阿莱倪士
2014/01/09
0
0

没有更多内容

加载失败,请刷新页面

加载更多

IT兄弟连 Java语法教程 流程控制语句 循环结构语句3

while循环 Java中的另外一种循环是while循环。while循环的语法格式如下: while(条件表达式){ 循环体; } 其中条件表达式定义了控制循环的条件,可以使任何有效的boolean表达式,条件为真时,...

老码农的一亩三分地
24分钟前
0
0
OSChina 周四乱弹 —— 你们倒是救驾啊,别笑啦

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @RISYOII :#今日歌曲推荐# 一荤一素 太年轻的人 他总是不满足 固执地不愿停下 远行的脚步 望着高高的天 走了长长的路 忘了回头看 她有没有哭...

小小编辑
今天
1K
11
idea下springboot 项目在static目录下添加文件不生效

idea下springboot 项目在static目录下添加文件不生效 问题描述 是这样子的,我的项目目录结构如下: 我在static目录下,创建了index.html和aaaa.jpg这两个文件。然后,启动服务访问 http://l...

wotrd
昨天
5
0
k8s1.14 一、环境

1. 4台虚拟机 (CentOS Linux release 7.2.1511 (Core) ) 192.168.130.211 master 192.168.130.212 node1 192.168.130.213 node2 192.168.130.214 node3 2. 设置服务器hostname 2.1 设置本机......

ThomasCheng
昨天
4
0
盖茨:如果我现在开创一家公司 将会专注于AI

新浪科技讯,北京时间 6 月 26 日凌晨消息,微软联合创始人比尔·盖茨(Bill Gates)在周一接受采访时表示,如果他今天从哈佛大学辍学并开创一家新公司,那么这家公司将会专注于人工智能(A...

linuxCool
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部