大概有半年多没有写博客了,主要是最近忙于工做,也没来得及与你们分享技术。固然如今的技术大多都有人写其博客分享了,也找不到合适写的,因此也就懒得写了。最近在SpringSource上看到了一好玩的东东,因而就照着官方的文档玩了一把,再根据本身的理解写给博友们品鉴,若有不对的地方,欢迎博友们提出,笔者会一一记录下来,以便后续改进。java
Spring Data是SpringSource基金会下的一个用于简化数据库访问,并支持云服务的开源框架。其主要目标是使得数据库的访问变得方便快捷,并支持map-reduce框架和云计算数据服务。对于拥有海量数据的项目,能够用Spring Data来简化项目的开发。
然而针对不一样的数据储存访问使用相对的类库来操做访问。Spring Data中已经为咱们提供了不少业务中经常使用的一些接口和实现类来帮咱们快速构建项目,好比分页、排序、DAO一些经常使用的操做。
今天主要是对Spring Data下的JPA模块进行讲解。
为何说Spring Data能帮助咱们快速构建项目呢,由于Spring Data已经在数据库访问层上帮咱们实现了公用功能了,而咱们只需写一个接口去继承Spring Data提供给咱们接口,即可实现对数据库的访问及操做,相似于spring-orm的TemplateDAO。spring
----------------------------------------------邪恶的分割-------------------------------------------------------数据库
public interface Repository<T, ID extends Serializable> { }
这个接口只是一个空的接口,目的是为了统一全部Repository的类型,其接口类型使用了泛型,泛型参数中T表明实体类型,ID则是实体中id的类型。c#
再来看一下Repository的直接子接口CrudRepository中的方法:app
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的框架中将每一个类的颗粒度都划得很细,这主要也是为了责任分离。ide
----------------------------------------------邪恶的分割线------------------------------------------------------单元测试
针对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为咱们提供的注解查询。