Spring ORM+Hibernate?Out!换 Spring Data JPA 吧!
2019独角兽企业重金招聘Python工程师标准>>>
转载请注明出处:http://blog.csdn.net/anxpp/article/details/51415698,谢谢!
在一切开始之前,先举个简单的例子,以提高大家的兴致!
如果一张表user有三个字段,id、name和age,要查找指定姓氏在某年龄以上的user,在传统的Spring+Hibernate中,dao层我们是这样写的:
UserDao:
public interface UserDao{ List<User> findByNameLikeAndAgeGreaterThan(String firstName,Integer age); }
UserDaoImpl(已经是相对简单的HibernateTemplate方式了):
public class UserDaoImpl implements UserDao{ @Override public List<User> findByFirstNameAndAge(String firstName, Integer age) { //具体hql查找:"from User where name like '%'"+firstName + "and age > " + age; return hibernateTemplateMysql.execute(new HibernateCallback() { @Override public Object doInHibernate(Session session) throws HibernateException { String hql = "from User where name like '?' and age > ?"; Query query = session.createQuery(hql); query.setParameter(0, firstName+""); query.setParameter(1, age); return query.uniqueResult(); } }); } }
然而,如果我们用Spring Data JPA呢:
public interface UserDao extends JpaRepository<User, Serializable>{ List<User> findByNameLikeAndAgeGreaterThan(String firstName,Integer age); }
对,就这样,已经没有了,连实现都不需要写的!service直接调用UserDao.findByNameLikeAndAgeGreaterThan(firstName+"%",age)即可。
那么,下面就来介绍,Spring Data JPA是个什么,如何为我们简化JPA开发。
推荐两篇文章:
- JPA规范介绍及实例(Java数据持久化解决方案)
- 本文重点做介绍,如果需要了解如何搭建,请参考这篇文章:手把手教你从最基本的Java工程搭建SpringMVC+SpringDataJPA+Hibernate(含源码下载)(Spring Data JPA详细搭建过程)
1、简介
官网:http://projects.spring.io/spring-data-jpa/
Spring Data是一个用于简化数据库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷,并支持map-reduce框架和云计算数据服务。
Spring Data 包含多个子项目:
JPA就是其中子项目之一,正如JPA规范介绍及实例中的介绍,JPA属于重量级的,因为它需要运行在JAVA EE容器中,而Spring Data JPA提供了轻量级的实现,在任何servlet容器中就可以运行。
Spring Data JPA相对于Java EE中的JPA,更加简洁易用:
- 配置更简单。
- Spring以轻量级的方式实现了部分在 EJB 容器环境下才具有的功能。
- Spring 将 EntityManager 的创建与销毁、事务管理等代码抽取出来,并由其统一管理。
- 正如前面的例子,极大的简化了数据库访问层(dao)的代码。
Spring Data JPA 让一切近乎完美!Spring 对 JPA 的支持非常强大,开发者只需关心核心业务逻辑的实现代码,无需过多关注 EntityManager 的创建、事务处理等 JPA 相关的处理,这基本上也是作为一个开发框架而言所能做到的极限了。至此,开发者连仅剩的实现持久层业务逻辑的工作都省了,唯一要做的,就只是声明持久层的接口,其他都交给 Spring Data JPA 来帮你完成!
2、开发步骤
Spring Data JPA 进行持久层开发一般分三个步骤:
- 声明持久层的接口,该接口继承 Repository(或Repository的子接口,其中定义了一些常用的增删改查,以及分页相关的方法)。
- 在接口中声明需要的业务方法。Spring Data 将根据给定的策略生成实现代码。
- 在 Spring 配置文件中增加一行声明,让 Spring 为声明的接口创建代理对象。配置了 <jpa:repositories> 后,Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象。
3、主要的接口
- Repository: 仅仅是一个标识,没有任何方法,方便Spring自动扫描识别
- CrudRepository: 继承Repository,实现了一组CRUD相关的方法
- PagingAndSortingRepository: 继承CrudRepository,实现了一组分页排序相关的方法
- JpaRepository: 继承PagingAndSortingRepository,实现一组JPA规范相关的方法
(根据一个最小什么的原则,一下想不起了)我们通常优先考虑Repository接口,因为它已经足以完成大多时候的使用了,如果有更多的需求,再依次选择其子接口。
我们的dao层接口继承Repository后,如无更多需要,可以不用写任何代码,仅仅是一个空的接口就行了。
同时,也可以选择不继承接口,而使用注解方式,比如,下面两种方式是完全一样的:
public interface UserDao extends Repository<AccountInfo, Long> {}
@RepositoryDefinition(domainClass = AccountInfo.class, idClass = Long.class) public interface UserDao {}
4、创建查询的方法
4.1、Query creation from method names(解析方法名)
如文首例子中的 findByNameLikeAndAgeGreaterThan(String firstName,Integer age) 方法一样,我们从方法名就知道想要做什么,而此时,Spring也知道... 所以它为我们创建了对应的查询,而不需要我们多些一段代码。如何做到的呢:
框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。并且如果方法的最后一个参数是 Sort 或者 Pageable 类型,也会提取相关的信息,以便按规则进行排序或者分页查询。
如上面的findByNameLikeAndAgeGreaterThan,解析步骤如下:
1、剔除findBy
2、先判断 nameLikeAndAgeGreaterThan(根据 POJO 规范,首字母变为小写)是否为 User 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则进行下一步。
3、从右往左截取第一个大写字母开头的字符串(此处为Than),然后检查剩下的字符串是否为 User 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第2步,继续从右往左截取;知道找出 name 为 User 的属性。
4、从已截取的部分后面开始,重新从第1步开始(剔除条件语句),循环忘后,直到整个方法名处理完毕。
5、通过获取的操作、条件和属性,带入参数的值,生成查询。
在查询时,通常需要同时根据多个属性进行查询,提供的一些表达条件查询的关键字完整的表:
如果截取的时候,因为Entity的一些属性刚好碰到有歧义的时候,这种方式的可读性就不是太好了。
4.2、通过 @Query 创建查询
如果想要更好的可读性,使用@Query 创建查询也是一种很好的方法。
比如文首的例子,我们可以这样改写:
public interface UserDao extends JpaRepository<User, Serializable>{ @Query("select * from User u where u.name like ?1 and u.age>?2") List<User> findByNameLikeAndAgeGreaterThan(String firstName,Integer age); }
出了使用?+数字的方式代替参数,还可以使用如下方式:
public interface UserDao extends JpaRepository<User, Serializable>{ @Query("select * from User u where u.name like :first and u.age>:age") List<User> findByNameLikeAndAgeGreaterThan(@Param("first")String firstName,@Param("age")Integer age); }
@Query 也可以用来修改和删除等,加上@Modifying即可:
@Modifying @Query("update User u set u.name = ?1 where u.id = ?2") public int increaseSalary(String name, int id);
可以如下加上参数以使用原生查询:
public interface UserRepository extends JpaRepository<User, Long> { @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true) User findByEmailAddress(String emailAddress); }
4.3、JPA NamedQueries(命名查询)
有两种方式,一是通过xml配置:
<named-query name="User.findByLastname"> <query>select u from User u where u.lastname = ?1</query> </named-query>
另一种是通过注解:
@Entity @NamedQuery(name = "User.findByEmailAddress",query = "select u from User u where u.emailAddress = ?1") public class User {}
如果要使用上面编写的两个查询,只需要dao层接口生命方法即可:
public interface UserDao extends JpaRepository<User, Long> { List<User> findByLastname(String lastname); User findByEmailAddress(String emailAddress); }
对于查询的创建,会有限查找是否有命名查询,而不是根据方法名创建查询。
4.4、查询创建策略
- CREATE:企图建构从查询方法名特定存储查询。在一般的方法是从该方法名中移除一组特定前缀和解析方法的其余部分。其他两种创建查询的方法会被忽略。
- USE_DECLARED_QUERY:如果方法通过 @Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则抛出异常。
- CREATE_IF_NOT_FOUND(默认):如果方法通过 @Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则通过解析方法名字来创建查询。
4.5、为接口中部分方法提供自定义实现
虽然 Spring Data JPA 已为我们提供了如此强大的功能,但有时候,可能我们还是希望能自定义一些查询的实现。
自定义实现一般又两种方法:
方法一:
- 将需要手动实现的方法从持久层接口(比如 UserDao )中抽取出来,独立成一个新的接口(比如 UserDaoMore ),并让 UserDao 继承 UserDaoMore
- 为 UserDaoMore 提供自定义实现(比如 UserDaoMoreImpl )
- 将 UserDaoMoreImpl 配置为 Spring Bean
- 在 <jpa:repositories> 中如下配置:
<jpa:repositories base-package="com.anxpp.demo.core.dao"> <jpa:repository id="UserDao" repository-impl-ref=" UserDaoMore" /> </jpa:repositories> <bean id="UserDaoMore" class="..."/>
方法二:
设置自动查找时默认的自定义实现类命名规则,用以指定实现类的后缀:
<jpa:repositories base-package="com.anxpp.demo.core.dao" repository-impl-postfix="Impl"/>
在框架扫描到 UserDao 接口时,它将尝试在相同的包目录下查找 UserDaoImpl.java,如果找到,便将其中的实现方法作为最终生成的代理类中相应方法的实现。
5、调用存储过程
如果需要调用存储过程,可以通过如下方式:
首先在实体上定义:
@Entity @NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class), @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) }) public class User {}
显示的调用存储过程:
@Procedure("plus1inout") Integer explicitlyNamedPlus1inout(Integer arg);
通过别名隐示的调用:
@Procedure("plus1inout") Integer explicitlyNamedPlus1inout(Integer arg);
或者通过实体的注解调用:
@Procedure(name = "User.plus1IO") Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg);
以及通过方法名的隐示调用:
Procedure Integer plus1(@Param("arg") Integer arg);
6、Query by Example
Example API 主要由三部分组成:
- Probe: That is the actual example of a domain object with populated fields.
- ExampleMatcher: The ExampleMatcher carries details on how to match particular fields. It can be reused across multiple Examples.
- Example: An Example consists of the probe and the ExampleMatcher. It is used to create the query.
何时使用:
- 查询您的数据存储与一组静态或动态约束
- 可以对对象频繁的重构,而无需担心破坏现有的查询
- 从底层数据存储API独立工作
限制:
- 查询谓词使用and连接
- 不支持条件嵌套(如 firstname = ?0 or (firstname = ?1 and lastname = ?2))
- starts/contains/ends/regex 仅支持 String类型,exact 仅支持其他类型。
示例实体:
public class Person { @Id private String id; private String firstname; private String lastname; private Address address; // … getters and setters omitted }
这一块暂时不忙研究了... 感觉写完睡觉了,有时间再好好看看。我猜有点像享元模式,不过扩展了很多东西。
7、事务
默认情况下,Spring Data JPA 实现的方法都是使用事务的。
针对查询类型的方法,其等价于 @Transactional(readOnly=true)。
增删改类型的方法,等价于 @Transactional。可以看出,除了将查询的方法设为只读事务外,其他事务属性均采用默认值。
也可以自定义事务:
public interface UserRepository extends CrudRepository<User, Long> { @Override @Transactional(timeout = 10) public List<User> findAll(); }
可以将事务注解到service的一个方法上:
@Service class UserManagementImpl implements UserManagement { private final UserRepository userRepository; private final RoleRepository roleRepository; @Autowired public UserManagementImpl(UserRepository userRepository, RoleRepository roleRepository) { this.userRepository = userRepository; this.roleRepository = roleRepository; } @Transactional public void addRoleToAllUsers(String roleName) { Role role = roleRepository.findByName(roleName); for (User user : userRepository.findAll()) { user.addRole(role); userRepository.save(user); } }
注意:需要<tx:annotation-driven /> 或使用了 @EnableTransactionManagement
可以在查询的方法前面注解事务:
@Transactional(readOnly = true) public interface UserRepository extends JpaRepository<User, Long> { List<User> findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void deleteInactiveUsers(); }
这里的deleteInactiveUsers()方法因为的事务注解覆盖了整体的readOnly,所以这个方法不是只读的。
8、附录
查询关键字:
查询的返回值类型:
小结:本文大概的介绍了Spring Data JPA,如果需要了解如何搭建相关项目,请参考:手把手教你从最基本的Java工程搭建SpringMVC+SpringDataJPA+Hibernate(含源码下载)
转载于:https://my.oschina.net/lvzunwei/blog/687841
Spring ORM+Hibernate?Out!换 Spring Data JPA 吧!相关推荐
- spring ORM是什么,spring的七大模块有哪些
Spring有七大功能模块,分别是Spring Core,AOP,ORM,DAO,MVC,WEB,Context. 1,Spring Core Core模块是Spring的核心类库,Spring的所有 ...
- Spring ORM示例 - JPA,Hibernate,Transaction
Spring ORM示例 - JPA,Hibernate,Transaction 欢迎来到Spring ORM示例教程.今天我们将使用Hibernate JPA事务管理来研究Spring ORM示例. ...
- Spring Data JPA教程
在Java类或对象与关系数据库之间管理数据是一项非常繁琐且棘手的任务. DAO层通常包含许多样板代码,应简化这些样板代码,以减少代码行数并使代码可重复使用. 在本教程中,我们将讨论Spring数据的J ...
- Primefaces Spring和Hibernate集成示例教程
Primefaces Spring和Hibernate集成示例教程 欢迎使用Spring Primefaces和Hibernate Integration示例.框架之间的集成是一项复杂的任务,而且大多 ...
- ORM框架之Spring Data JPA(一)Hibernate实现JPA规范
一.ORM简述 ORM(Object-Relational Mapping) 表示对象关系映射.在面向对象的软件开发中,通过ORM,就可以把对象映射到关系型数据库中.只要有一套程序能够做到建立对象与数 ...
- Hibernate、JPA、Spring Data JPA,傻傻分不清
国庆假期接近尾声,明天最后一天了,要开始收收心啦- 今天讲讲一个初学者(或许一些老手)可能没去搞懂的几个概念:Hibernate.JPA.Spring Data JPA 之间的关联. 嘿嘿,前段时间有 ...
- hibernate mysql 读写分离_SpringBoot集成Spring Data JPA及读写分离
JPA是什么 JPA(Java Persistence API)是Sun官方提出的Java持久化规范,它为Java开发人员提供了一种对象/关联映射工具 来管理Java应用中的关系数据.它包括以下几方面 ...
- 简述 JPA 与 Spring Data JPA 与 Hibernate
1.JPA是什么?以及相关概述 JPA的是 Java Persistence API 的简写,是Sun官方提出的一种ORM规范! Sun提出此规范有2个原因: 1.简化现有Java EE和Java S ...
- ORM框架之Spring Data JPA(三)高级查询---复杂查询
一.spring data jpa高级查询 1.1Specifications动态查询 有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data ...
最新文章
- 解决Windows Installer的错误
- 从一个数组中找出最接近目标_LeetCode每日一题 | 转变数组后最接近目标值的数组和...
- JNative用法注意事项
- 中国太阳能窗户市场趋势报告、技术动态创新及市场预测
- jacob 详解 语音_JAVA 实现Jacob语音播报
- python之show、hide、slidedonw、slideup方法实例
- 多线程 NSThread 的使用
- AUTOCAD——三种箭头的画法
- 雅虎Yahoo 前段优化 14条军规
- 港科报道 | 8位校友入选香港25青年科创先锋人物
- 关于各类图形CAD底层内核
- maven离线(offline)构建时无法找到本地依赖 Non-resolvable import POM: Cannot access nexus
- javaWeb用户注册之用户名、密码、邮箱验证例题
- 100条经典C++笔试题目及答案分享
- 《zw版·Halcon-delphi系列原创教程》 Halcon分类函数014,tuple,元组
- C# 插件式程序开发
- iOS 利用AFNetworking实现大文件分片上传
- Unity内置资源如何打包避免冗余
- 爱情不是等你有空才珍惜
- 触摸板-Thinkpad E440禁用触摸板
热门文章
- linux命令结果中获取某一列,Linux中 ls -l 命令显示结果中的每一列的含义
- java 方法详解_Java方法详解
- nbear分页 效率低_为什么大家都说“SELECT *”效率低?
- python统计英文单词个数_python统计英文文本中的回文单词数
- nuxt打包路劲问题_简述Nuxt.js
- 10怎么读_孩子学习浑浑噩噩,做事拖拖拉拉,老是做不完作业,家长怎么办?...
- Streams API
- mysql默认值是随机数_mysql生成指定位数的随机数及批量生成随机数的方法
- npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! node-sass@
- centos怎么编写java_编写的java程序在centos后台运行的方法