• 解决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);
        }
    
    }
  • 相关阅读:
    最小生成树——prim
    最短路径——floyd(多源最短路径)
    最短路径——Dijkstra(简易版)
    图的遍历——BFS(队列实现)
    图的遍历——DFS(邻接矩阵)
    图的创建——十字链表
    图的创建——邻接表法
    图的创建——邻接矩阵
    队列——链表实现
    队列——数组实现(循环队列)
  • 原文地址:https://www.cnblogs.com/sweetchildomine/p/8627214.html
Copyright © 2020-2023  润新知