session进阶 spring管理jpa事务
集成了一些小主流的框架,事务的问题就暴露出来了,用的以前的常规配置,做些查询没啥问题,做插入的时候本来也没发现,后来无意中看日志文件,竟然说是挂起了当前事务重新开启了spring的事务才执行持久化数据库的操作的。
jpa用的entityManagerFactory来管理带annotations的vo,但是用到了spring,一分析就觉得不合理,事务交给spring作为主控者来管理就比较好。于是乎获取通过
sessionFactory获取session的时候重写了获取session的方式:
protected Session getSession(SessionFactory sessionFactory)
throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true); session.setFlushMode(FlushMode.AUTO);
return session;
}
这时候问题又出现了,查询木有问题,操作数据库的时候说这个事务是readOnly的小看了下sessiofactory的源码。原来这个拿过来的sessionspring会把它默认的事务传播方式设置成只读的,上jboss的鸟语官网看了下里面合理的思路:
sessionFactory注入给jpa的entityManagerFactory再发entityManagerFactory注入给spring的transactionManager
这样进行事务托管就比较合理了。然后再配置一下aop:
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true" />
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="edit*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="*" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="allServiceMethod"
expression="execution(* com.wizriver.service.impl..*.*(..))" />
<!-- <aop:pointcut id="cabinServiceMethod"-->
<!-- expression="execution(* com.wizriver.service.impl..*.*(..))" />--> <!-- <aop:pointcut id="gboatServiceMethod"-->
<!-- expression="execution(* com.wizriver.service.impl..*.*(..))" />--> <aop:advisor pointcut-ref="allServiceMethod" advice-ref="txAdvice" />
<!-- <aop:advisor pointcut-ref="cabinServiceMethod" advice-ref="txAdvice" />-->
<!-- <aop:advisor pointcut-ref="gboatServiceMethod" advice-ref="txAdvice" />--> </aop:config>
这样定义好事务的切面后就已经差不多哦拉了。然后具体业务中对特殊要求再重新顶一下事务的传播行为类型就哦拉。
再次测试后看日志事务只被挂起当前的业务中
转自无为斋的空间
第二篇:spring data jpa实战
Spring data jpa 实战
一、
二、
三、
四、
五、
六、 概述 .................................................................................................................. 2 配置maven ....................................................................................................... 2 配置持久化单元 ................................................................................................ 5 配置applicationContext.xml文件...................................................................... 6 spring data jpa详解 ................................................................................ 7 spring data jpa 1.1新特性及分页介绍 ............................................................. 11
一、 概述
对于我们以前实现数据操作层,我们大部分是通过手工来创建,创建的过程一般分为下面几个步骤:
1. 创建一个具有公共增、删、改、查及分页的基类dao接口
2. 创建实现基类dao接口及各个方法的抽象类
3. 创建具体的dao层,并继承基类dao接口
4. 实现具体的dao层,继承基类的抽象类
现在我们有更好的方法替代这些工作了,spring Data家族给我们提供了一个现成的dao层框架,这里面有不同的项目,如Spring Data JPA, Spring Data Neo4j and Spring Data MongoDB,他们的共同特点是他们给我们提供了框架代码,不再需要我们自己去实现了。
而且,spring Data能自动创建实体dao的实现类和自定义查询。因此基于以前写得程序,我们几乎不再需要第四步骤了。
在这一章我们会学到如何利用spring Datajpa来调整我们的dao层。我们的项目是基于maven实现的,所以首先我们将配置pom,然后我们再进入实际的仓库代码。
二、 配置maven
在maven中的pom中加入如下的依赖
<properties>
<spring.version>3.1.1.RELEASE</spring.version> <hibernate.version>3.6.10.Final</hibernate.version> <spring.data.version>1.0.3.RELEASE</spring.data.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version>
</dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency>
<groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>${spring.data.version}</version> </dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId> <version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId> <version>${hibernate.version}</version>
</dependency>
<dependency> <groupId>servletapi</groupId> <artifactId>servletapi</artifactId> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.20</version> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
<version>2.4</version> <scope>provided</scope> </dependency> <dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.12</version> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> </dependency>
三、 配置持久化单元
在类路径底下新建META-INF,在META-INF中建一个persistence.xml,其内容如下: <?xml version="1.0"?>
<persistence xmlns="/xml/ns/persistence"
xmlns:xsi="/2001/XMLSchema-instance"
xsi:schemaLocation="/xml/ns/persistence
/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<!-- file:///D:\hibernate\hibernate-entitymanager-3.3.2.CR1\resources\org\hibernate\ejb\persistence_1_0.xsd -->
<persistence-unit name="spring" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties>
<property name="hibernate.max_fetch_depth" value="3"/> <property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.jdbc.fetch_size" value="18"/> <property name="hibernate.jdbc.batch_size" value="10"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> <!-- <property name="hibernate.cache.provider_class" <property name="hibernate.cache.use_second_level_cache" <property name="hibernate.cache.use_query_cache" value="org.hibernate.cache.SingletonEhCacheProvider"/> value="true"/>
value="true"/>
-->
</properties>
</persistence-unit>
</persistence>
四、 配置applicationContext.xml文件
在resources加入spring的配置文件applicationContext.xml,日志配置和jdbc配置详情请参考源码。applicationContext.xml中的内容如下
<beans xmlns="/schema/beans"
xmlns:jee="/schema/jee"
xmlns:jpa="/schema/data/jpa" xmlns:p="/schema/p"
xmlns:tx="/schema/tx"
xmlns:xsi="/2001/XMLSchema-instance"
xsi:schemaLocation="
/schema/beans
/schema/beans/spring-beans-3.1.xsd /schema/data/jpa
/schema/data/jpa/spring-jpa-1.0.xsd /schema/jee
/schema/jee/spring-jee-3.1.xsd
/schema/tx
/schema/tx/spring-tx-3.1.xsd">
<bean
class="org.springframework.beans.factory.config.PropertyPlacehold <property name="locations"> <list> erConfigurer"> <value>classpath:jdbc.properties</value> <bean id="dataSource" </property> </list> </bean>
class="mons.dbcp.BasicDataSource"
destroy-method="close">
/>
<property name="url" value="${jdbc.url}" /> <property name="driverClassName" value="${jdbc.driverClassName}"
<property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:persistenceUnitName="spring"
p:dataSource-ref="dataSource" />
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory" />
<jpa:repositories base-package="com.example.runbooks.repo" />
<tx:annotation-driven />
<jpa:repositories base-package="com.pango.spring.dao"/>
</beans>
其中: <jpa:repositories base-package="com.pango.spring.dao"/> 在服务启动时,将dao层接口通过动态代理加入到容器管理中。即类似于我们以前配置的@service然后通过组建扫描机制,加入到容器管理中。
五、 spring data jpa详解
接下去我们来写spring data jpa的代码,让 Spring Data JPA 来帮助我们完成业务逻辑。在着手写代码之前,开发者需要先 下载Spring Data JPA 的发布包(需要同时下载 Spring Data Commons 和 Spring Data JPA 两个发布包,Commons 是 Spring Data 的公共基础包),并把相关的依赖 JAR 文件加入到 CLASSPATH 中。
首先我们来看看Spring data jpa执行过程如下:
在看上面Bean定义的时候,其实已经明白了执行过程:
1. 将JPA CRUD规范相关的方法交给SimpleJpaRepository这个类执行
2. 将特殊查询相关的交给QueryExecutorMethodInterceptor执行。主要做自定义实现的部分,method query部分和named query部分。
具体查询类详见下图。
第二、我们来看接口类
public interface UserDao extends JpaRepository<User, Integer> { public void findByUserName(String userName);
}
}
通过上面的列子,我们来总结一下使用spring Data jpa 进行持久化开发大致需要的三个步骤:
1. 声明持久层的接口,该接口继承 Repository,Repository 是一个标记型接口,它不包含任何方法,当然如果有需要,Spring Data 也提供了若干 Repository 子接口,其中定义了一些常用的增删改查(CrudRepository),以及分页相关的方法
(PagingAndSortingRepository),当然在我们的列子中我们实现了JpaRepository。
2. 在接口中声明需要的业务方法。Spring Data 将根据给定的策略(具体策略稍后讲解)
来为其生成实现代码。
3. 在 Spring 配置文件中增加一行声明,让 Spring 为声明的接口创建代理对象。配置了
<jpa:repositories> 后,Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象。
对于上面的接口我们还可以有一种写法,即通过注解
@RepositoryDefinition(domainClass = User.class, idClass = Integer.class) public interface UserDao{
} public void findByUserName(String userName);
效果与上面通过接口实现一样。
前面提到,Spring Data JPA 在后台为持久层接口创建代理对象时,会解析方法名字,并实现相应的功能。除了通过方法名字以外,它还可以通过如下两种方式指定查询语句:
1. Spring Data JPA 可以访问 JPA 命名查询语句。开发者只需要在定义命名查询语句时,
为其指定一个符合给定格式的名字,Spring Data JPA 便会在创建代理对象时,使用该命名查询语句来实现其功能。
2. 开发者还可以直接在声明的方法上面使用 @Query 注解,并提供一个查询语句作为参数,
Spring Data JPA 在创建代理对象时,便以提供的查询语句来实现其功能。
下面我们分别讲述三种创建查询的方式。
? 根据方法名来查询,即我们刚开始给出的列子
public List<User> findByUserName(String userName);
框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 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 中的 "<",比如 findBySalaryLessThan(int max); GreaterThan --- 等价于 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<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数; NotIn --- 等价于 SQL 中的 "not in",比如
findByUsernameNotIn(Collection<String> userList) ,方法的参数可以是
Collection 类型,也可以是数组或者不定长参数;
? 使用 @Query 创建查询
@Query("select u from User u where u.userName=?1 ")
public List<User> findByQuery(String userName);
我们可以看到使用@Query非常的简单,就通常写jpql语句一样。
? JPA 命名查询语句创建查询
首先创建命名查询,如下
@Entity
@NamedQuery(name="User.findName",query="select u from User u where u.userName=?")
public class User implements Serializable {
}
这里需要注意的名称,名称需要满足”DomainClass.methodName()”的命名规则,或者出现异常。
@Query("select u from User u where u.userName=?1 ")
public List<User> findByQuery(String userName); /**
? 创建查询的顺序
Spring Data JPA 在为接口创建代理对象时,如果发现同时存在多种上述情况可用,它该优先采用哪种策略呢?为此,<jpa:repositories> 提供了 query-lookup-strategy 属性,用以指定查找的顺序。它有如下三个取值:
?
? create --- 通过解析方法名字来创建查询。即使有符合的命名查询,或者方法通过 @Query 指定的查询语句,都将会被忽略。 create-if-not-found --- 如果方法通过 @Query 指定了查询语句,则使用该语句实现
查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则通过解析方 法名字来创建查询。这是
query-lookup-strategy 属性的默认值。
use-declared-query --- 如果方法通过 @Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则抛出异常
六、 spring data jpa 1.1新特性及分页介绍
? 在spring data jpa1.1中加入了对原生sql的支持,但是这个功能还不是很完
善,具有一定的局限性。对于动态的分页和排序查询还不支持。个人感觉这个功能并不是很好用,spring data jpa有待完善。
@Query(value="select * from user t where t.userName=?",nativeQuery=true)
需要在后面加入nativeQuery=true 即表示原生查询
? 用于查询生成的新关键字: LessThanEqual, GreaterThanEqual, Before, After, StartsWith,
EndsWith, Contains
? CDI 集成 (see here for details)
CDI上下文依赖注入,在javaEE 6引入了这个概念,即 (Contexts and Dependency Injection for the Java EE platform)规范,简称CDI。CDI规范吸收了来自、和的 最佳实践,并与Java EE开发的实际需要相结合。正如CDI的字面含义一样,CDI中的两个核心功能是上下文信息(context)和依赖注入。这两个功能的结合点是Java 中基本的组件模型bean。在CDI中,bean 定义了应用的状态和逻辑,并由容器来进行管理。每个被管理的bean都有定义好的绑定到特定上下文的作用域和生命周期。当需要注入或访问bean时,容器 会从作用域对应的上下文中获取。当作用域失效时,对应上下文中所有的对象都会被删除。CDI中的每个bean都可以作为依赖注入时的目标。
CDI中预定义了一些常用的作用域。默认的作用域是Dependent,表示只对被注入的对象生效。作用域ApplicationScoped表示 应用的全局作用域,用来创建全局唯一的对象。RequestScoped和SessionScoped则与HTTP相关,分别表示HTTP请求和HTTP 会话。ConversationScoped是由应用自定义生命周期长短的作用域,可以用来实现跨多页面的工作流。
? 分页查询
Spring data jpa 对分页做了封装,主要的类有Pageable和Page
@Query("select a from User a where a.balance > ?1")
public Page<User> findByBalanceGreaterThan( Integer balance,Pageable pageable);
需要传入的参数
PageRequest request =
new PageRequest(1, 5, Sort.Direction.DESC, "id"); Page<User> users = userDao.findByBalanceGreaterThan(100, request);
多值排序可以使用下面这个pageRequest PageRequest request2 = new PageRequest(1, 5, new Sort(
new Order(Direction.ASC, "lastName"), new Order(Direction.DESC, "salary") ));