以前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衝突的問題)html
如今想了一下也是愚蠢,咱們應該吸取Spring data jpa 優秀的思想,使用Spring data 快速開發,選擇性地放棄一些功能,用Mybatis管理長sql,揚長避短,因此整合一了Mybatis + jpa的抽象通用service,怎麼封裝抽象整合Mybatis,這裏就不講了java
mongo版本 http://www.cnblogs.com/sweetchildomine/p/7729319.html
mysql
完整的pom.xmlweb
<?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>
首先創建一個抽象類redis
/** * 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裏spring
/** * 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裏面獲取當前發起操做的用戶信息sql
/** * 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配置類裏已經存在此註解mongodb
/**
* 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 Beanapache
/**
* 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
測試類,而後去看看是否版本號,以及建立人,建立時間,修改人,修改時間都有了session
@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); } }