在spring项目中,使用jfinal的db功能,不知道怎样配置才能使事物正常

sphsyv 发布于 2016/03/30 13:50
阅读 1K+
收藏 3

@JFinal 你好,想跟你请教个问题:

目前是老项目,spring mvc+hibernate编写的,现在想不影响老项目的情况下,把jFinal的DB功能加进去,以前的代码还是hibernate,新功能就使用jFinal的db

目前的的配置如下:

<bean id="dataSource" name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
		<!-- 这一项可配可不配,如果不配置druid会根据url自动识别dbType, 然后选择相应的driverClassName -->
		<property name="driverClassName" value="${jdbc.driverClassName}" />
		<!-- 配置这个属性的意义在于,如果存在多个数据源,监控的时候 可以通过名字来区分开来。如果没有配置,将会生成一个名字, 格式是:"DataSource-" + System.identityHashCode(this) -->
		<property name="name" value="DataSource-autolink" />
		<!-- 连接数据库的url -->
		<property name="url" value="${jdbc.url}" />
		<!-- 连接数据库的用户名 -->
		<property name="username" value="${jdbc.username}" />
		<!-- 连接数据库的密码。如果你不希望密码直接写在配置文件中, 可以使用ConfigFilter -->
		<property name="password" value="${jdbc.password}" />

		<!-- 初始化时建立物理连接的个数。初始化发生在显示调用init方法, 或者第一次getConnection时 -->
		<property name="initialSize" value="1" />
		<!-- 连接池最大连接数量 -->
		<property name="maxActive" value="20" />
		<!-- 连接池最小连接数量 -->
		<property name="minIdle" value="1" />
		<!-- 获取连接时最大等待时间,单位毫秒。配置了maxWait之后, 缺省启用公平锁,并发效率会有所下降, 如果需要可以通过配置useUnfairLock属性为true使用非公平锁 -->
		<property name="maxWait" value="60000" />

		<!-- 是否缓存preparedStatement,也就是PSCache。 PSCache对支持游标的数据库性能提升巨大,比如说oracle。 在mysql5.5以下的版本中没有PSCache功能,建议关闭掉。 5.5及以上版本有PSCache,建议开启 -->
		<property name="poolPreparedStatements" value="true" />
		<!-- 要启用PSCache,必须配置大于0,当大于0时, poolPreparedStatements自动触发修改为true。 在Druid中,不会存在Oracle下PSCache占用内存过多的问题, 可以把这个数值配置大一些,比如说100 -->
		<property name="maxOpenPreparedStatements" value="100" />

		<!-- 用来检测连接是否有效的sql,要求是一个查询语句。 如果validationQuery为null,testOnBorrow、testOnReturn、 testWhileIdle都不会其作用。在mysql中通常为select 'x',在oracle中通常为 select 1 from dual -->
		<property name="validationQuery" value="${hibernate.validationQuery}" />
		<!-- 物理连接初始化的时候执行的sql -->
		<property name="connectionInitSqls" value="${hibernate.validationQuery}" />
		<!-- 申请连接时执行validationQuery检测连接是否有效, 做了这个配置会降低性能 -->
		<property name="testOnBorrow" value="false" />
		<!-- 归还连接时执行validationQuery检测连接是否有效, 做了这个配置会降低性能 -->
		<property name="testOnReturn" value="false" />
		<!-- 建议配置为true,不影响性能,并且保证安全性。 申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis, 执行validationQuery检测连接是否有效 -->
		<property name="testWhileIdle" value="true" />

		<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
		<!-- 有两个含义: 1) Destroy线程会检测连接的间隔时间 2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明 -->
		<property name="timeBetweenEvictionRunsMillis" value="60000" />
		<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
		<!-- Destory线程中如果检测到当前连接的最后活跃时间和当前时间的差值大于 minEvictableIdleTimeMillis,则关闭当前连接 -->
		<property name="minEvictableIdleTimeMillis" value="300000" />

		<!-- 是否打开removeAbandoned功能,对于建立时间超过removeAbandonedTimeout的连接强制关闭 -->
		<property name="removeAbandoned" value="false" />
		<!-- 多少秒删除连接,秒为单位,指定连接建立多长时间就需要被强制关闭,如果removeAbandoned为false,这个设置项不再起作用 -->
		<property name="removeAbandonedTimeout" value="28800" />
		<!-- 关闭abanded连接时输出错误日志,指定发生removeAbandoned的时候,是否记录当前线程的堆栈信息到日志中 -->
		<property name="logAbandoned" value="true" />

		<!-- 属性类型是字符串,通过别名的方式配置扩展插件, 常用的插件有: 监控统计用的stat 日志用的log4j 防御sql注入的wall -->
		<!-- 保存DruidDataSource的监控记录,DruidDataSource会定期把监控数据输出到日志中 -->
		<property name="timeBetweenLogStatsMillis" value="300000" />
	</bean>

	<!-- spring 事务管理 ,ActiveRecordPlugin可以获得此 dataSource 可以把事务交给spring 管理 -->
	<bean id="dataSourceProxy" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
		<property name="targetDataSource" ref="dataSource" />
	</bean>
	<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSourceProxy"></property>
	</bean>
	<!-- 拦截器方式配置事物 -->
	<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="*" propagation="REQUIRED" />
		</tx:attributes>
	</tx:advice>
	<!-- 把事务控制在Service层 -->
	<aop:config>
		<aop:pointcut id="transactionPointcut" expression="execution(* other.**.service..*Impl.*(..)))" />
		<aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
	</aop:config>

<bean id="activeRecordConfig" class="other.ActiveRecordConfig"></bean>



package other;

import javax.annotation.Resource;
import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;

import com.jfinal.plugin.activerecord.ActiveRecordPlugin;

public class ActiveRecordConfig {

	@Resource(name = "dataSource")
	private DataSource dataSource;

	@Bean(initMethod = "start", destroyMethod = "stop")
	public ActiveRecordPlugin init() {
		ActiveRecordPlugin arp = new ActiveRecordPlugin(dataSource);
		return arp;
	}

}





package jfinal.test;

import com.jfinal.aop.Before;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.tx.Tx;

public class TestServiceImpl implements TestServiceI {

	@Override
	public void save() {
		Db.update("insert into a set id = '2'");
		Db.update("insert into b set id = '1'");
	}

}



这种配置,只能让我再调用service方法的时候,正确的执行,但是事物没有控制住。

波总有没有这方面的解决方案?

加载中
2
如梦技术
如梦技术

其实可以写个spring Aop,然后把tx里面的代码copy过去就ok了,这样比较优雅!

代码见: http://git.oschina.net/596392912/codes

查看:JFinalTxAop.java 和 JFinalTx.java,这样之后就能按照spring的习惯来了。

@JFinal 帮我顶一下

1
JFinal
JFinal

   最简单的方法是直接使用 jfinal 的 AOP,大致这样:

// 业务类
public MyService {
  // 声明事务拦截器
  @Before(Tx.class)
  public void justDoIt() {
     ......
  }
}

// 使用业务层 AOP
MyService service = Duang.duang(MyService.class);
service.justDoIt();

   如果业务层不希望动任何代码,也可以使用inject interceptor来应用事务:

OtherService service = Duang.duang(OtherService.class, Tx.class);
service.doIt();
1
s
sphsyv

引用来自“JFinal”的评论

   最简单的方法是直接使用 jfinal 的 AOP,大致这样:

// 业务类
public MyService {
  // 声明事务拦截器
  @Before(Tx.class)
  public void justDoIt() {
     ......
  }
}

// 使用业务层 AOP
MyService service = Duang.duang(MyService.class);
service.justDoIt();

   如果业务层不希望动任何代码,也可以使用inject interceptor来应用事务:

OtherService service = Duang.duang(OtherService.class, Tx.class);
service.doIt();

就是想用配置的方式去做。避免每个controller里都

XxxServiceI xxxService = Enhancer.enhance(XxxServiceImpl.class, Tx.class);
xxxService.save();

这样做的话,service里面的get方法是不是也加上了事物?get方法只是select * from xxx,不需要事物。

还有,如果有多个controller使用了相同的xxxService,那么这些controller是不是都要执行一下

XxxServiceI xxxService = Enhancer.enhance(XxxServiceImpl.class, Tx.class);


JFinal
JFinal
如果想用配置,可以使用 spring 拦截器,然后添加点 Db.tx(....) 这样的方法,将目标方法包裹起来即可
JFinal
JFinal
如果是用@Before(Tx.class) 为特定的方法加事务,不会影响到其它方法,如果是inject interceptor的方式,可以使用 Duang.duang(target, new TxByRegex("(.*update.*|.*save.*)") 这样来做
1
s
sphsyv

@JFinal

我用这种方式解决了,但是代码可能不够优雅。

先自己定义一个MyTxByMethodRegex


package other;

import java.sql.SQLException;
import java.util.regex.Pattern;

import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.kit.LogKit;
import com.jfinal.plugin.activerecord.Config;
import com.jfinal.plugin.activerecord.DbKit;
import com.jfinal.plugin.activerecord.DbPro;
import com.jfinal.plugin.activerecord.IAtom;
import com.jfinal.plugin.activerecord.tx.Tx;

public class MyTxByMethodRegex implements Interceptor {

	private Pattern pattern = Pattern.compile("(.*save.*|.*update.*|.*delete.*)");

	public void intercept(final Invocation inv) {
		Config config = Tx.getConfigWithTxConfig(inv);
		if (config == null) {
			config = DbKit.getConfig();
		}
		LogKit.info(inv.getMethodName());
		if (pattern.matcher(inv.getMethodName()).matches()) {
			DbPro.use(config.getName()).tx(new IAtom() {
				public boolean run() throws SQLException {
					inv.invoke();
					return true;
				}
			});
		} else {
			inv.invoke();
		}
	}
}

service不用做改动,也不需要写@Before(Tx.class)

package other;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.jfinal.aop.Enhancer;
import com.jfinal.kit.JsonKit;
import com.jfinal.kit.LogKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.plugin.activerecord.Record;

import other.service.TestServiceI;
import other.service.TestServiceImpl;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:spring-jfinal.xml")
public class TestSpringJfinalTransaction2 {

	private TestServiceI testServiceEnhancer = Enhancer.enhance(TestServiceImpl.class, MyTxByMethodRegex.class);

	@Test
	public void t2() {
		testServiceEnhancer.save();
	}

}



controller可以写
private TestServiceI testServiceEnhancer = Enhancer.enhance(TestServiceImpl.class, MyTxByMethodRegex.class);
下面调用的时候,如果不是save/update/delete这种,就不会开启事物。



JFinal
JFinal
临时传入正则这样用: Duang.duang(target, new TxByMethodRegex("(.*update.*|.*save.*)");
JFinal
JFinal
如果TestService 是无状态的,可以在controller 中加上 static 关键字,可以避免每次new出对象,另外 MyTxByRegexMethod 这样的功能jfinal已经提供了,直接使用一下:com.jfinal.plugin.activerecord.tx.TxByMethodRegex 即可,使用的时候可以临时传入正则,提升灵活性
返回顶部
顶部