Spring Data JPA快速使用_第1页
Spring Data JPA快速使用_第2页
Spring Data JPA快速使用_第3页
Spring Data JPA快速使用_第4页
Spring Data JPA快速使用_第5页
已阅读5页,还剩18页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Spring Data JPA快速启动目录Spring Data JPA快速启动11概述12 Spring Data JPA使用12.1实体类规范:22.2 Dao接口规范:22.3 自定义查询42.4 配置文件规范62.5事务处理92.6 为接口中的部分方法提供自定义实现92.7 锁102.8 应用场景以及优点112.9 JPA的缺陷:111概述Spring Data JPA 是Spring Data 家族的提供的一个持久层框架,可以自动创建dao实现类和自定义查询,简化了持久层代码。2 Spring Data JPA使用使用Spring Data JPA只需要3个步骤:(1)声明持久层的接口,该接口继承 Repository,Repository 是一个标记型接口,它不包含任何方法,当然如果有需要,Spring Data 也提供了若干 Repository 子接口,其中定义了一些常用的增删改查,以及分页相关的方法。(2)在接口中声明需要的业务方法。Spring Data 将根据给定的策略来为其生成实现代码。(3)在 Spring 配置文件中增加一行声明,让 Spring 为声明的接口创建代理对象。配置了 后,Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象。此外, 还提供了一些属性和子标签,便于做更细粒度的控制。可以在 内部使用 、 来过滤掉一些不希望被扫描到的接口。2.1实体类规范:实体类均使用注解进行配置,常用注解描述如下,其他请参考JPA配置规范:Entity:在bean的最上面使用,使实体bean映射到数据库中 . Table(name=tableName):指定实体bean映射到数据库中的表名. Id GeneratedValue(strategy=GenerationType.AUTO) Id:指定实体bean在数据库表的实体标识属性 Strategy:id的生成策略. GenerationType.AUTO:自动(默认值,可以不设定) GenerationType.IDENTITY:数据库的id自增长(Oracle不支持) GenerationType. SEQUENCE:序列化(Mysql不支持) GenerationType.TABLE:表生成 Column(length=20,nullable=false,name=) :字段属性注释 Length:指定字段的长度 Nullable:是否为空.false:不为空;true:空 Name:指定字段映射到表的列名Temporal(TemporalType.DATE):指定类型为时间的字段属性的映射 DATE:日期,不包括时间.1985-09-09 TIME:时间,没有日期.23:44:00 TIMESTAMP:时间戳,包括日期和时间 1985-09-09 23:44:00 Enumerated(EnumType.STRING):枚举类型属性的映射 EnumType.STRING:保存枚举类型数据的字符类型数据到数据库 EnumType.ORDINAL:保存枚举类型数据的索引值到数据库 Lob:大文本字段(String)和二进制数组类型字段(Byte)使用它,对应数据库的大文本类型.Transient : 属性不跟数据库表进行映射使用Transient注解 Basic(fetch=FetchType.LAZY): 延时加载数据使用Basic注解中的FatchType:LAZY 懒加载;EAGER 立即2.2 Dao接口规范:Dao接口需要继承BaseDaoe.g:public interface PersonDao extends BaseDaoPerson:Dao对应实体类类型Long:Dao对应实体类主键ID类型import java.io.Serializable;import java.util.List;import org.springframework.data.domain.Page;import org.springframework.data.domain.Pageable;import org.springframework.data.domain.Sort;import org.springframework.data.repository.Repository;public interface BaseDao extends Repository BaseDao中提供了若干默认方法,可以直接进行调用,描述如下:1、 public List save(Iterable entities)/批量新增或编辑2、 public T save(T entity); / 保存或更新2、public T delete(T entity); / 根据ID删除3、public T findOne(ID id); / 根据ID查询4、public boolean exists(ID id); / 根据ID查找是否存在5、public List findAll(); / 查询所有6、public List findAll(Iterable ids); / 根据ID集合查找7、public long count(); / 查询记录总数8、public Page findAll(Pageable pageable); / 分页查询参数pageable:可以传递new PageRequest(0, 10)0表示第一页10表示每页10条数据返回值Page对象使用:当前页:getNumber();每页显示条目数:getSize();总页数:getTotalPages();结果集总数量:getTotalElements();是否是首页:isFirstPage();是否是末页:isLastPage();是否有上一页:hasPreviousPage();是否是下一页:hasNextPage();查询结果集:getContent();9、public List findAll(Sort sort); / 排序查询参数sort可以传递:new Sort(new Order(Direction.DESC, personId), new Order(Direction.ASC, age)Direction.DESC:降序排列Direction.ASC:生序排列personId和age:列名其他方法可以进行自定义,方式参考代码范例2.3 自定义查询支持两种模式的自定义查询:(1) 使用 Query 创建查询Query 注解的使用非常简单,只需在声明的方法上面标注该注解,同时提供一个 JP QL 查询语句即可,如下所示:Query 支持命名参数示例 public interface UserDao extends BaseDao public AccountInfo save(AccountInfo accountInfo); /与参数的顺序无关 Query(from AccountInfo a where a.accountId = :id) public AccountInfo findByAccountId(Param(id)Long accountId); /与参数的顺序相关 Query(from AccountInfo a where a.accountId = ?1) public AccountInfo findById(Long accountId); 此外,也可以通过使用 Query 来执行一个更新操作,为此,我们需要在使用 Query 的同时,用 Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询。如下所示:使用 Modifying 将查询标识为修改查询 Modifying Query(update AccountInfo a set a.salary = ?1 where a.salary ?2) public int increaseSalary(int after, int before); 方法中的参数顺序与Query中的变量顺序保持一致。(2)通过解析方法名创建查询例1. 根据命名规则写对应的抽象方法即可.根据 username 查找 user.只需在 UserDao 中 填写如下方法. Java代码1. publicListfindByUsername(Stringusername);例2. 根据两个属性查询. Java代码1. publicListfindByUsernameAndPassword(Stringusername,Stringpassword);框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。并且如果方法的最后一个参数是 Sort 或者 Pageable 类型,也会提取相关的信息,以便按规则进行排序或者分页查询。在创建查询时,我们通过在方法名中使用属性名称来表达,比如 findByUserAddressZip ()。框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,详细规则如下(此处假设该方法针对的域对象为 AccountInfo 类型): 先判断 userAddressZip (根据 POJO 规范,首字母变为小写,下同)是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步; 从 右往左截取第一个大写字母开头的字符串(此处为 Zip),然后检查剩下的字符串是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设 user 为 AccountInfo 的一个属性; 接 着处理剩下部分( AddressZip ),先判断 user 所对应的类型是否有 addressZip 属性,如果有,则表示该方法最终是根据 AccountInfo.user.addressZip 的取值进行查询;否则继续按照步骤 2 的规则从右往左截取,最终表示根据 AccountInfo.user.address.zip 的值进行查询。可 能会存在一种特殊情况,比如 AccountInfo 包含一个 user 的属性,也有一个 userAddress 属性,此时会存在混淆。读者可以明确在属性之间加上 _ 以显式表达意图,比如 findByUser_AddressZip() 或者 findByUserAddress_Zip()。在查询时,通常需要同时根据多个属性进行查询,且查询的条件也格式各样(大于某个值、在某个范围等等),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 中的 ,比如 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 类型,也可以是数组或者不定长参数;创建查询的顺序Spring Data JPA 在为接口创建代理对象时,如果发现同时存在多种上述情况可用,它该优先采用哪种策略呢?为此, 提供了 query-lookup-strategy 属性,用以指定查找的顺序。它有如下三个取值: create - 通过解析方法名字来创建查询。即使有符合的命名查询,或者方法通过 Query 指定的查询语句,都将会被忽略。 create-if-not-found - 如果方法通过 Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则通过解析方 法名字来创建查询。这是 query-lookup-strategy 属性的默认值。 use-declared-query - 如果方法通过 Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则抛出异常。2.4 配置文件规范persistence.xml路径:classpathMETA-INFpersistence.xml内容:cn.damai.student.core.domain.Coursecn.damai.student.core.domain.StudentCoursecn.damai.student.core.domain.Studentcn.damai.student.core.domain.Teachercn.damai.student.core.domain.Departmentorg.hibernate.ejb.HibernatePersistence java:/MySqlDS applicationContext.xml2.5事务处理可以在业务层(Service)使用spring事务。默认情况下,Spring Data JPA 实现的方法都是使用事务的。针对查询类型的方法,其等价于 Transactional(readOnly=true);增删改类型的方法,等价于 Transactional。可以看出,除了将查询的方法设为只读事务外,其他事务属性均采用默认值。如果用户觉得有必要,可以在接口方法上使用 Transactional 显式指定事务属性,该值覆盖 Spring Data JPA 提供的默认值。同时,也可以在业务层方法上使用 Transactional 指定事务属性,这主要针对一个业务层方法多次调用持久层方法的情况。持久层的事务会根据设置的事务传播行为来决定是挂起业务层事务还是加入业务层的事务。Transactional只能被应用到public方法上, 对于其它非public的方法,如果标记了Transactional也不会报错,但方法没有事务功能.Spring使用声明式事务处理,默认情况下,如果被注解的数据库操作方法中发生了unchecked异常,所有的数据库操作将rollback;如果发生的异常是checked异常,默认情况下数据库操作还是会提交的。这种默认的行为是可以改变的。使用Transactional注解的noRollbackFor和rollbackFor属性如:Transactional(rollbackFor=Exception.class)可以使checked异常发生时,数据库操作也rollback、Transactional(noRollbackFor=RuntimeException.class)可以使unchecked异常发生时也提交数据库操作。也可以使用noRollbackForClassName、rollbackForClassName属性来指定一个异常类名的String数组来改变默认的行为。读取数据库中的数据时是不需要事务管理的,这种情况下可以使用事务的传播行为来告诉Spring不需要开启事务,如:Transactional(propagation = Propagation.NOT_SUPPORTED)。事务的传播行为有:1.REQUIRED:表示业务方法需要在一个事务中处理,如果业务方法执行时已经在一个事务中,则加入该事务,否则重新开启一个事务。这也是默认的事务传播行为;2. NOT_SUPPORTED:声明业务方法不需要事务,如果业务方法执行时已经在一个事务中,则事务被挂起,等方法执行完毕后,事务恢复进行;3. REQUIRES_NEW:表明业务方法需要在一个单独的事务中进行,如果业务方法进行时已经在一个事务中,则这个事务被挂起,并重新开启一个事务来执行这个业务方法,业务方法执行完毕后,原来的事务恢复进行;4. MANDATORY:该属性指定业务方法只能在一个已经存在的事务中进行,业务方法不能发起自己的事务;如果业务方法没有在一个既有的事务中进行,容器将抛出异常;5. SUPPORTS:该属性指定,如果业务方法在一个既有的事务中进行,则加入该事务;否则,业务方法将在一个没有事务的环境下进行;6. NEVER:指定业务方法不可以在事务中进行,如果业务方法执行时已经在一个事务中,容器将抛出异常;7. NESTED:该属性指定,如果业务方法在一个既有的事务中执行,则该业务方法将在一个嵌套的事务中进行;否则,按照REQUEIRED来对待。它使用一 个单独的事务,这个事务可以有多个rollback点,内部事务的rollback对外部事务没有影响,但外部事务的rollback会导致内部事务的 rollback。这个行为只对DataSourceTransactionManager有效。/事务传播属性 Transactional(propagation=Propagation.REQUIRED) /如果有事务,那么加入事务,没有的话新建一个(不写的情况下) Transactional(propagation=Propagation.NOT_SUPPORTED) /容器不为这个方法开启事务 Transactional(propagation=Propagation.REQUIRES_NEW) /不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务 Transactional(propagation=Propagation.MANDATORY) /必须在一个已有的事务中执行,否则抛出异常 Transactional(propagation=Propagation.NEVER) /必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反) Transactional(propagation=Propagation.SUPPORTS) /如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务. Transactional(propagation=Propagation.NESTED) Transactional (propagation = Propagation.REQUIRED,readOnly=true) /readOnly=true只读,不能更新,删除 Transactional (propagation = Propagation.REQUIRED,timeout=30)/设置超时时间 Transactional (propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT)/设置数据库隔离级别用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException(注释);)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception(注释);)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚 ,如下:Transactional(rollbackFor=Exception.class) /指定回滚,遇到异常Exception时回滚 public void methodName() throw new Exception(注释); Transactional(noRollbackFor=Exception.class)/指定不回滚,遇到运行期例外(throw new RuntimeException(注释);)会回滚 public ItimDaoImpl getItemDaoImpl() throw new RuntimeException(注释); 2.6 为接口中的部分方法提供自定义实现有些时候,可能需要在某些方法中做一些特殊的处理,此时自动生成的代理对象不能完全满足要求。为了能够为部分方法提供自定义实现,我们可以采用如下的方法: 将需要手动实现的方法从持久层接口(假设为 PersonDao )中抽取出来,独立成一个新的接口(假设为PersonDaoCustom ); 为 PersonDaoCustom 提供自定义实现(假设为 PersonDaoCustomImpl ); 将 PersonDaoCustomImpl 配置为 Spring Bean; 在 中指定实现类的后缀(比如“CustomImpl”)。 提供了一个 repository-impl-postfix 属性,用以指定实现类的后缀。假设做了如下配置:设置自动查找时默认的自定义实现类命名规则 则在框架扫描到 PersonDao 接口时,它将尝试在相同的包目录下查找 PersonDaoCustomImpl.java,如果找到,便将其中的实现方法作为最终生成的代理类中相应方法的实现。一般情况下我们如下进行配置,把实现类的包纳入jpa扫描路径: 2.7 锁 支持悲观锁 锁是处理数据库事务并发的一种技术,当两个或更多数据库事务并发地访问相同数据时,锁可以保证同一时间只有一个事务可以修改数据。锁的方法通常有两种:乐观锁和悲观锁。乐观锁认为多个并发事务之间很少出现冲突,也就是说不会经常出现同一时间读取或修改相同数据,在乐观锁中,其目标是 让并发事务自由地同时得到处理,而不是发现或预防冲突。两个事务在同一时刻可以访问相同的数据,但为了预防冲突,需要对数据执行一次检查,检。 悲观锁认为事务会经常发生冲突,在悲观锁中,读取数据的事务会锁定数据,在前面的事务提交之前,其它事务都不能修改数据。JPA 1.0只支持乐观锁,你可以使用EntityManager类的lock()方法指定锁模式的值,可以是READ或WRITE,如: 引用EntityManager em = . ; em.lock (p1, READ); 对于READ锁模式,JPA实体管理器在事务提交前都会锁定实体,检查实体的版本属性确定实体自上次被读取以来是否有更新,如果版本属性被更新了,实体管理器会抛出一个OptimisticLockException异常,并回滚事务。对于WRITE锁模式,实体管理器执行和READ锁模式相同的乐观锁操作,但它也会更新实体的版本列。 JPA 2.0增加了6种新的锁模式,其中两个是乐观锁。JPA 2.0也允许悲观锁,并增加了3种悲观锁,第6种锁模式是无锁。下面是新增的两个乐观锁模式: 引用 1、OPTIMISTIC:它和READ锁模式相同,JPA 2.0仍然支持READ锁模式,但明确指出在新应用程序中推荐使用OPTIMISTIC。 2、OPTIMISTIC_FORCE_INCREMENT:它和WRITE锁模式相同,JPA 2.0仍然支持WRITE锁模式,但明确指出在新应用程序中推荐使用OPTIMISTIC_FORCE_INCREMENT。 下面是新增的三个悲观锁模式: 引用 1、PESSIMISTIC_READ:只要事务读实体,实体管理器就锁定实体,直到事务完成锁才会解开,当你想使用重复读语义查询数据时使用这种锁模式,换句话说就是,当你想确保数据在连续读期间不被修改,这种锁模式不会阻碍其它事务读取数据。 2、PESSIMISTIC_WRITE:只要事务更新实体,实体管理器就会锁定实体,这种锁模式强制尝试修改实体数据的事务串行化,当多个并发更新事务出现更新失败几率较高时使用这种锁模式。 3、PESSIMISTIC_FORCE_INCREMENT:当事务读实体时,实体管理器就锁定实体,当事务结束时会增加实体的版本属性,即使实体没有修改。 你也可以指定新的锁模式NONE,在这种情况下表示没有锁发生。 JPA 2.0也提供了多种方法为实体指定锁模式,你可以使用EntityManager的lock() 和 find()方法指定锁模式。此外,EntityManager.refresh()方法可以恢复实体实例的状态。 下面的代码显示了使用PESSIMISTIC_WRITE锁模式的悲观锁: 引用/ read Part p = em.find(Part.class, pId); / lock and refresh before update em.refresh(p, PESSIMISTIC_WRITE); int pAmount = p.getAmount(); p.setAmount(pAmount - uCount); 在这个例子中,它首先读取一些数据,然后应用PESSIMISTIC_WRITE锁,在更新数据之前调用 EntityManager.refresh()方法,当事务更新实体时,PESSIMISTIC_WRITE锁锁定实体,其它事务就不能更新相同的实 体,直到前面的事务提交。JPA默认应用层将使用乐观锁机制使用乐观锁时entity必须声明一个version属性,该字段的值由持久化框架自动维护,应用层不能修改。持久化框架可以引入其它机制实现乐观锁检查,或者实现更细粒度的乐观锁控制,JPA不做要求JPA的乐观锁机制是基于数据行的JPA中声明了Lock Mode,可以实现悲观锁效果,至于实现方式不作具体要求,只要确保不存在P1: dirty read、P2: non-repeatable read就行LockModeType有READ和WRITE 2种对于WRITE模式,在isolation level为read commited情况下数据库可以确保不会出现P1、P2两种状况READ模式一般使用数据库的锁实现,用它实现悲观锁效果可以使用 Lock , 里面填入的是 JPA 的 LockModeType * Lock(LockModeType.PESSIMISTIC_READ)public List findByUsername(String username);对应的 sql 就是: select * from t_user where username=? lock in share mod

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论