最近在做毕业设计,其中涉及到一些知识图谱的内容,打算用Neo4j来存储知识图谱内容,与知识图谱无关的数据打算使用MySQL存储,为了后需方便开发,在一开始创建项目的时候就把依赖全部添加进来了,配置文件也都写好了。
就在系统的基本功能快完成的时候,打算测试一下对于MySQL操作的部分有没有bug,一番折腾下来,发现SpringBoot的事务没有生效,百度+Google找了一堆解决方法,都不起作用,于是拿出大杀器——单步调试,看看到底是执行到哪里出了问题。最终执行到下面图片上这行代码,恍然大悟。
没有走MySQL的事务,走的是Neo4j的事务,那MySQL里的数据肯定不会回滚。说白了就是SpringBoot的事务还不够智能,没法识别多个数据源。
问题找到了,那就好解决了,但万万没想到网上能搜到的MySQL+Neo4j多数据源事务有关的内容很旧,现在Spring Data Neo4j早就更新了,里面的API变化了很多,所以只能去查看官方文档,找找事务相关的内容。虽然网上现有的示例代码不能用,但其解决方案的思路还是可以用的,最后这个问题终于是解决了,下面记录一下解决方法。
解决方法
思路很简单,自定义一个事务管理器,然后在里面维护多个数据源的事务就好了。下面以我目前的环境为例用代码实现,其他多数据源情况大同小异。
TransactionConfig.java
import org.neo4j.driver.Driver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import javax.persistence.EntityManagerFactory;
@Configuration
public class TransactionConfig {
/**
* 定义neo4j事务管理器
*
* @param databaseNameProvider
* @param driver
* @return
*/
@Bean("neo4jTransactionManager")
public Neo4jTransactionManager neo4jTransactionManager(Driver driver,
DatabaseSelectionProvider databaseNameProvider) {
return new Neo4jTransactionManager(driver, databaseNameProvider);
}
/**
* 定义mysql事务管理器,必须有transactionManager作为默认事务管理器
*
* @param emf
* @return
*/
@Bean("transactionManager")
public JpaTransactionManager jpaTransactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
MultiTransaction.java
自定义注解,方便后续使用,因为我有些地方只需要操作MySQL,有些地方只需要操作Neo4j,还有些地方要两个数据库同时操作,所以我自定义了三个注解,分别是MultiTransaction、MySQLTransaction、Neo4jTransaction,三个注解是西安方法一样,这里只给出MultiTransaction的实现。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MultiTransaction {
}
TransactionAspect.java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@Aspect
@Component
public class TransactionAspect {
@Autowired
@Qualifier("neo4jTransactionManager")
Neo4jTransactionManager neo4jTransactionManager;
@Autowired
@Qualifier("transactionManager")
JpaTransactionManager jpaTransactionManager;
@Around("@annotation(MultiTransaction)")
public Object multiTransaction(ProceedingJoinPoint proceedingJoinPoint) {
TransactionStatus neo4jTransactionStatus = neo4jTransactionManager
.getTransaction(new DefaultTransactionDefinition());
TransactionStatus jpaTransactionStatus = jpaTransactionManager
.getTransaction(new DefaultTransactionDefinition());
try {
Object obj = proceedingJoinPoint.proceed();
// 事务之间必须是包含关系,不能交叉
jpaTransactionManager.commit(jpaTransactionStatus);
neo4jTransactionManager.commit(neo4jTransactionStatus);
return obj;
} catch (Throwable throwable) {
jpaTransactionManager.rollback(jpaTransactionStatus);
neo4jTransactionManager.rollback(neo4jTransactionStatus);
throw new RuntimeException(throwable);
}
}
@Around("@annotation(MySQLTransaction)")
public Object mySQLTransaction(ProceedingJoinPoint proceedingJoinPoint) {
TransactionStatus jpaTransactionStatus = jpaTransactionManager
.getTransaction(new DefaultTransactionDefinition());
try {
Object obj = proceedingJoinPoint.proceed();
jpaTransactionManager.commit(jpaTransactionStatus);
return obj;
} catch (Throwable throwable) {
jpaTransactionManager.rollback(jpaTransactionStatus);
throw new RuntimeException(throwable);
}
}
@Around("@annotation(Neo4jTransaction)")
public Object neo4jTransaction(ProceedingJoinPoint proceedingJoinPoint) {
TransactionStatus neo4jTransactionStatus = neo4jTransactionManager
.getTransaction(new DefaultTransactionDefinition());
try {
Object obj = proceedingJoinPoint.proceed();
neo4jTransactionManager.commit(neo4jTransactionStatus);
return obj;
} catch (Throwable throwable) {
neo4jTransactionManager.rollback(neo4jTransactionStatus);
throw new RuntimeException(throwable);
}
}
}
这样只要在需要开启事务的方法上添加相应的注解,就可以确保发生异常时回滚了。比如:
@MultiTransaction
public Object insert(Object obj) throws Exception {
// do somthing...
}
H
大佬能不能告诉以下SDN和Driver的版本
海加尔金鹰
多数据源应该算是分布式事务了吧
迟於
@海加尔金鹰 : 确实