解决neo4j @Transactional 与Spring data jpa @Transactional 冲突问题,@CreatedBy,@CreatedDate,@LastModifiedBy,@LastModifiedDate,以及解决@Version失效问题
之前mybatis特别流行,所以前几个项目都是用@SelectProvider,@InsertProvider,@UpdateProvider,@DeleteProvider 加反射泛型封装了一些通用方法,虽然小伙伴表示使用得比较满意,但是我认为对他们的发展不太好,可以学习一些主流的大厂框架.同时也为把Spring cloud 升级为Finchley.M8,Spring boot 升级为 2.0 ,所以又看了一遍Spring data JPA,之前看了Spring data jpa 觉得 Specification 可读性特别不好,也特别不好理解,就没有深究了,这次再回头看Spring data jpa 加入了一些可以细化控制的锁机制,而且用了Spring data mongo后,也挺喜欢对象持久化监听事件,并且对事务注解的增强(刚好最近使用Neo4J做权限模块,这解决了TransactionManager冲突的问题)
现在想了一下也是愚蠢,我们应该吸收Spring data jpa 优秀的思想,使用Spring data 快速开发,选择性地放弃一些功能,用Mybatis管理长sql,扬长避短,所以整合一了Mybatis + jpa的抽象通用service,怎么封装抽象整合Mybatis,这里就不讲了
mongo版本http://www.cnblogs.com/sweetchildomine/p/7729319.html
完整的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>org.lzw</groupId>
<artifactId>web</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>web</name>
<description>Demo project for Spring Boot</description> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.M8</spring-cloud.version>
<skipTests>true</skipTests>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
<!--</dependency>--> <!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> <!-- redis session -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency> <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency> <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency> <!-- jbatis begin --> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency> <dependency>
<groupId>org.lzw</groupId>
<artifactId>jbatis</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.0.0.RELEASE</version>
</dependency> <dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency> <!-- jbatis end --> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> </dependencies> <dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> <repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories> </project>
首先建立一个抽象类
/**
* Created by laizhenwei on 2018/3/21
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Accessors(chain = true)
@MappedSuperclass
public abstract class AbstractJbatisEntity implements Serializable {
private static final long serialVersionUID = 8413473756947412760L; @Id
@GenericGenerator(name="idGenerator", strategy="uuid") //这个是hibernate的注解/生成32位UUID
@GeneratedValue(generator="idGenerator")
private String id; }
再建立一个包含@CreatedBy,@CreatedDate,@LastModifiedBy,@LastModifiedDate注解的抽象类 @Version 不写在父类里,并不是每个表都需要乐观锁,哪个表需要,就加在某个pojo里
/**
* Created by laizhenwei on 2018/3/21
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@MappedSuperclass
@Accessors(chain = true)
@EntityListeners(AuditingEntityListener.class)
public class AbstractAuditingEntity extends AbstractJbatisEntity { @CreatedBy
private String creator; @CreatedDate
private LocalDateTime createTime; @LastModifiedBy
private String modifier; @LastModifiedDate
private LocalDateTime modifyTime; }
实现AuditorAware,监听实体持久化操作,以便从Spring Security 的ContextHolder里面获取当前发起操作的用户信息
/**
* Created by laizhenwei on 2018/3/21
*/
public class SpringSecurityAuditorAware implements AuditorAware<String> { public Optional<String> getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()|| SecurityUtils.isAnonymous())
return Optional.of(SecurityUtils.ANONYMOUSUSER);
return Optional.ofNullable(((UserInfo) authentication.getPrincipal()).getUsername());
}
}
配置类这里之所以注释掉@EnableTransactionManagement,是因为在Neo4j配置类里已经存在此注解
/**
* Created by laizhenwei on 2018/3/21
*/
@Configuration
@EnableJpaAuditing
@EnableJpaRepositories(basePackages="org.lzw.web.repository.jbatis")
//@EnableTransactionManagement
public class JpaConfig{ @Autowired
private EntityManagerFactory entityManagerFactory; @Bean
public AuditorAware<String> auditorProvider() {
return new SpringSecurityAuditorAware();
} @Bean
public JpaTransactionManager transactionManager(){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
} }
Neo4j配置类,可见两个TransactionManager Bean 虽然类型不同,但是名字也不能相同,否则注入时会冲突,并且必须有其中一个名字为transactionManager,否则也会报找不到默认的TransactionManager Bean
/**
* Created by laizhenwei on 2018/3/21
*/
@Configuration
@EnableTransactionManagement
@EnableNeo4jRepositories(basePackages="org.lzw.web.repository.neo4j")
public class Neo4jPersistenceContext { @Bean
public SessionFactory getSessionFactory() {
SessionFactory sessionFactory = new SessionFactory(configuration(),"org.lzw.web.domain.neo4j");
return sessionFactory;
} @Bean
public Neo4jTransactionManager neo4jTransactionManager(){
return new Neo4jTransactionManager(getSessionFactory());
} @Bean
public org.neo4j.ogm.config.Configuration configuration() {
return new org.neo4j.ogm.config.Configuration.Builder()
.connectionLivenessCheckTimeout(50)
.connectionPoolSize(100)
.verifyConnection(true)
// .autoIndex("update")
.credentials("neo4j","root")
.uri("bolt://192.168.1.207:7687")
.build();
} }
userinfo pojo @Version
/**
* Created by laizhenwei on 2018/3/21
*/
@Entity
@Table(name = "tenant_user_info")
@Alias("UserInfo")
public class UserInfo extends AbstractAuditingEntity implements UserDetails { private String username; private String password; private boolean accountNonExpired; private boolean accountNonLocked; private boolean credentialsNonExpired; private boolean enabled; @Version
private Long version; @Transient
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
} @Override
public String getPassword() {
return this.password;
} @Override
public String getUsername() {
return this.username;
} @Override
public boolean isAccountNonExpired() {
return this.accountNonExpired;
} @Override
public boolean isAccountNonLocked() {
return this.accountNonLocked;
} @Override
public boolean isCredentialsNonExpired() {
return this.credentialsNonExpired;
} @Override
public boolean isEnabled() {
return this.enabled;
} public UserInfo setUsername(String username) {
this.username = username;
return this;
} public UserInfo setPassword(String password) {
this.password = password;
return this;
} public UserInfo setAccountNonExpired(boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
return this;
} public UserInfo setAccountNonLocked(boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
return this;
} public UserInfo setCredentialsNonExpired(boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
return this;
} public UserInfo setEnabled(boolean enabled) {
this.enabled = enabled;
return this;
} public Long getVersion() {
return version;
} public void setVersion(Long version) {
this.version = version;
} @Override
public boolean equals(Object o) {
if (!(o instanceof UserInfo))
return false;
if (o.toString().equals(this.getUsername()))
return true;
return false;
} @Override
public int hashCode() {
return this.getUsername().hashCode();
} @Override
public String toString() {
return this.getUsername();
} }
注解也被增强@Transactional(transactionManager="transactionManager",propagation = Propagation.REQUIRED) ,可以指定transactionManager,来解决多个transactionManager的问题.
@Version 乐观锁,之前再使用Spring data mongo 的时候,感觉很不错.但是开始在Spring data jpa 里面死活无效,version 字段一直为null,百思不得其解,再Spring data jpa 官方文档 居然搜索不到@Version注解,以为被弃用了
后来折腾了很久发现必须使用
@javax.persistence.Version 可以生效
而使用
@org.springframework.data.annotation.Version 不生效 因为Spring data mongo 就是用Spring的注解,并且认为Spring 都是遵守J2EE 标准的,潜意识觉得用两个注解都可以生效.
后来想想,@Version这乐观锁,是Hibernate实现的,而Hibernate才是Jpa规范的鼻祖,那么如果没有增加对@org.springframework.data.annotation.Version的支持,应该只会认@javax.persistence.Version
测试类,然后去看看是否版本号,以及创建人,创建时间,修改人,修改时间都有了
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserInfoServiceTest { @Resource
private UserInfoService userInfoService; @Test
@Rollback(false)
@Transactional(transactionManager="transactionManager",propagation = Propagation.REQUIRED)
public void saveTest(){
UserInfo userInfo = new UserInfo();
userInfo.setUsername("athos");
userInfo.setPassword("1232456");
userInfo.setEnabled(true);
userInfo.setCredentialsNonExpired(true);
userInfo.setAccountNonExpired(true);
userInfo.setAccountNonLocked(true);
userInfoService.saveAndFlush(userInfo);
UserInfo userInfo1 = userInfoService.getOne(userInfo.getId());
userInfo1.setPassword("123123");
userInfoService.saveAndFlush(userInfo1);
} }
解决neo4j @Transactional 与Spring data jpa @Transactional 冲突问题,@CreatedBy,@CreatedDate,@LastModifiedBy,@LastModifiedDate,以及解决@Version失效问题的更多相关文章
- 【spring data jpa】使用spring data jpa 的删除操作,需要加注解@Modifying @Transactional 否则报错如下: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call
使用spring data jpa 的删除操作,需要加注解@Modifying @Transactional 否则报错如下: No EntityManager with actual tran ...
- Spring data jpa JavassistLazyInitializer 不仅是Json序列化问题.以及解决办法
最近偷点时间更新一下框架,使用SpringBoot2.0 整套一起更新一下,发现些小问题 Spring data jpa getOne 返回的是代理对象,延迟加载的,ResponseBody成Json ...
- SpringBoot第九篇:整合Spring Data JPA
作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/10910059.html 版权声明:本文为博主原创文章,转载请附上博文链接! 前言 前面几章, ...
- 快速搭建springmvc+spring data jpa工程
一.前言 这里简单讲述一下如何快速使用springmvc和spring data jpa搭建后台开发工程,并提供了一个简单的demo作为参考. 二.创建maven工程 http://www.cnblo ...
- 深入浅出学Spring Data JPA
第一章:Spring Data JPA入门 Spring Data是什么 Spring Data是一个用于简化数据库访问,并支持云服务的开源框架.其主要目标是使得对数据的访问变得方便快捷,并支持map ...
- Spring Data JPA初使用(转载)
我们都知道Spring是一个非常优秀的JavaEE整合框架,它尽可能的减少我们开发的工作量和难度. 在持久层的业务逻辑方面,Spring开源组织又给我们带来了同样优秀的Spring Data JPA. ...
- Spring Data JPA初使用
我们都知道Spring是一个非常优秀的JavaEE整合框架,它尽可能的减少我们开发的工作量和难度. 在持久层的业务逻辑方面,Spring开源组织又给我们带来了同样优秀的Spring Data JPA. ...
- 干货|一文读懂 Spring Data Jpa!
有很多读者留言希望松哥能好好聊聊 Spring Data Jpa!其实这个话题松哥以前零零散散的介绍过,在我的书里也有介绍过,但是在公众号中还没和大伙聊过,因此本文就和大家来仔细聊聊 Spring D ...
- 【Spring】Spring Data JPA
原始JDBC操作数据库 传统JDBC方式实现数据库操作 package com.imooc.util; import java.io.InputStream; import java.sql.*; i ...
随机推荐
- Java如何中断正在运行的线程?
在Java编程中,如何中断正在运行的线程? 以下示例演示如何使用Thread类的interrupt()方法中断正在运行线程,并检查线程是否使用isInterrupted()方法中断. package ...
- Unity---------Mesh理解
Mesh顾名思义“网格”,Unity3D里面所有的模型都是由Mesh组成的,UI也不例外. 例如下图,模型上的一个个小网格就是Mesh,这些Mesh有不同的三维顶点(Vector3),共同组成了一个3 ...
- (原)linux下利用cmake来编译jthread开源库
其实上次在用hisi3531平台的时候,就已经编译过一次这个库了,这次换了平台环境,交叉编译器变成了arm-hisiv100-linux-工具链,所以,没办法只能重新来过. 因为之前编译过,所以这次还 ...
- 基于SOA的组件化业务基础平台[转]
转自https://www.ibm.com/developerworks/cn/webservices/1111_xiaojg_soa/index.html 业务基础平台是业务逻辑和基础架构平台之间的 ...
- Windows2008安装WebSphere 6.1提示此安装程序不能在图形方式中运行
推荐的解决方式:http://blog.sina.com.cn/s/blog_4b010fb50100n1mk.html 在一般情况下,安装Websphere是直接运行launchpad.exe,然后 ...
- python程序的输入输出(acm的几个小程序)
1, A+B Problem : http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=1000 #! ...
- log4net 日志配置及使用
一.log4net按照不同的[LEVEL]级别输出到不同文件 <log4net> <!--错误日志:::记录错误日志--> <!--按日期分割日志文件 一天一个--> ...
- fiddler 工具使用配置
前言: 之前为了手动测试项目组之间提供的接口,确定到底是哪一个接口出了问题.一般情况下,我们都直接采用了 Google 浏览器上,F12 后,Network 找到想要的 URL,然后,直接在浏览器上访 ...
- Java Number类
一般地,当需要使用数字的时候,我们通常使用内置数据类型,如:byte.int.long.double等. 例如: int i = 5000; float gpa = 13.65; byte mask ...
- SQLSERVER 2008 技术内幕 T-SQL查询 笔记1: SQL 执行顺序
与大多数语言一样,SQL语言也有一个执行顺序,只是在大多数编程语言中,代码是按照编写顺序来处理的,而在SQL中则不是,下图为SQL 执行顺序. () ) [ ALL | DISTINCT ] () [ ...