隨著 Docker 開始 Microservices 這名詞也開始常聽到,所以就想換用 SpringBoot 簡化設定並封裝成單一 Jar,Docker 化步驟就簡單很多,這邊使用 Mybatis 整合
Java
Java版本 8
資料庫
以下是我範例資料庫是用PostgreSQL
CREATE TABLE "public"."userinfo" (
"serno" bigserial NOT NULL PRIMARY KEY,
"usertype" int4 NOT NULL,
"userid" varchar(50) NULL,
"timecreate" date
)
MyBatis
因為使用jdk8所以驅動要抓jdbc41喔
這邊抓 PostgreSQL JDBC Driver
再來需要 mybatis-generator-core-1.3.2.jar
這邊抓 The MyBatis Blog
再準備設定檔
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 資料庫驅動jar -->
<classPathEntry location="./postgresql-9.4-1201.jdbc41.jar" />
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 去除自動生成的注釋 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 資料庫連接 -->
<jdbcConnection
driverClass="org.postgresql.Driver"
connectionURL="jdbc:postgresql://127.0.0.1:5432/test"
userId="root"
password="123456">
</jdbcConnection>
<!-- false:JDBC DECIMAL、NUMERIC類型解析為Integer,默認方式 -->
<!-- true:JDBC DECIMAL、NUMERIC類型解析為java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 生成模型的包名和位置 (可以自訂位址,但是路徑不存在不會自動創建使用Maven生成在target目錄下,會自動創建) -->
<javaModelGenerator targetPackage="com.sam.modules.entity"
targetProject="src">
<property name="enableSubPackages" value="false" />
<property name="trimStrings" value="true" /><!-- 從數據庫返回的值被清理前後的空格 -->
</javaModelGenerator>
<!-- 生成的映射文件包名和位置 -->
<sqlMapGenerator targetPackage="com.sam.modules.mappings"
targetProject="src">
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- 生成DAO的包名和位置 -->
<javaClientGenerator targetPackage="com.sam.modules.dao"
targetProject="src" type="ANNOTATEDMAPPER">
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- tableName:用於自動生成代碼的資料庫表;domainObjectName:對應於資料庫表的javaBean類名 -->
<table tableName="userinfo" schema="public" delimitIdentifiers="true" delimitAllColumns="true">
<!-- 此欄位使用自動增加的型態,不需要加入到SQL語法 -->
<generatedKey column="serno" sqlStatement="JDBC"/>
</table>
</context>
</generatorConfiguration>
跟之前比較不同的是 javaClientGenerator type 負責 orm 的部分由以前的 XMLMAPPER 改成 ANNOTATEDMAPPER,比較符合整個專案的風格
建立個src目錄再執行bat就可以產出了
REM set JAVA_HOME=D:\IDE\Java\jdk1.7.0_51
java -jar mybatis-generator-core-1.3.2.jar -configfile generatorConfig.xml -overwrite
Spring Boot
開一個新的 Gradle 專案
先看一下設定檔
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.4.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
sourceCompatibility = 1.8
version = '1.0'
jar {
manifest {
attributes 'Implementation-Title': 'Gradle Quickstart',
'Implementation-Version': version
attributes 'Main-Class': 'com.sam.app.Application'
}
baseName = 'springboot-all'
}
repositories {
mavenCentral()
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web:1.2.4.RELEASE',
'org.springframework.boot:spring-boot-starter-jdbc:1.2.4.RELEASE'
compile 'org.postgresql:postgresql:9.4-1201-jdbc41'
compile 'com.zaxxer:HikariCP:2.3.+'
compile 'org.mybatis:mybatis:3.3.0',
'org.mybatis:mybatis-spring:1.2.2'
testCompile group: 'junit', name: 'junit', version: '4.+'
}
spring-boot 就不用說了一定要的
這次改用了 HikariCP 這個 DataSource,就是強調快而已啦,感覺還很新可以關注一下
HikariCP. It's faster.
brettwooldridge/HikariCP · GitHub
再把剛剛產出的mybatis通通丟到專案內
Spring Boot的啟動程式
package com.sam.app;
import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.context.annotation.*;
//這邊使用 Java Class 作為設定,而非XML
@Configuration
//啟用 Spring Boot 自動配置,將自動判斷專案使用到的套件,建立相關的設定。
@EnableAutoConfiguration
//自動掃描 Spring Bean 元件
@ComponentScan( basePackages = {"com.sam"} )
//@SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
SpringBootApplication這注解我試不出來,就還是用原先的方法
再來是設定檔
package com.sam.app;
import javax.servlet.Filter;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.web.filter.CharacterEncodingFilter;
import com.zaxxer.hikari.*;
@Configuration
@MapperScan("com.sam.modules.dao")
//@PropertySources({ @PropertySource(value = "classpath:application.properties", ignoreResourceNotFound = true), @PropertySource(value = "file:./application.properties", ignoreResourceNotFound = true) })
public class AppConfig {
@Value("${name:}")
private String name;
// 用於處理編碼問題
@Bean
public Filter characterEncodingFilter() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
return characterEncodingFilter;
}
//HikariDataSourceConfiguration 有這種神奇的東西再來研究一下
@Bean(destroyMethod = "close")
public DataSource dataSource(){
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName("org.postgresql.Driver");
hikariConfig.setJdbcUrl("jdbc:postgresql://127.0.0.1:5432/test?charset=UTF8");
hikariConfig.setUsername("root");
hikariConfig.setPassword("abcdEF123456");
hikariConfig.setPoolName("springHikariCP");
//實驗結果是要把它關掉,不然事務控制會無效
hikariConfig.setAutoCommit(false);
hikariConfig.addDataSourceProperty("cachePrepStmts", "true");
hikariConfig.addDataSourceProperty("prepStmtCacheSize", "250");
hikariConfig.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
hikariConfig.addDataSourceProperty("useServerPrepStmts", "true");
hikariConfig.setMinimumIdle(20);
hikariConfig.setMaximumPoolSize(20);
hikariConfig.setConnectionInitSql("SELECT 1");
HikariDataSource dataSource = new HikariDataSource(hikariConfig);
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
return sessionFactory.getObject();
}
}
這邊 @MapperScan 是 mybatis 需要配置的,不然你沒辦法用ORM
再來 hikariConfig.setAutoCommit(false); 記得一定要關
因為這貨 autoCommit (default : true) 預設是開啊.....我需要事務回滾你Commit掉了我不知道怎麼回滾耶
JDBC Connection Pool [HikariCP] 設定 - 世界の一部
Controller
package com.sam.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.sam.modules.entity.Userinfo;
import com.sam.modules.service.UserinfoService;
@RestController
@RequestMapping(value = "/api")
public class AccountController {
@Autowired
private UserinfoService userinfoService;
@RequestMapping(value = "/userinfo", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE )
public ResponseEntity save(@RequestBody final Userinfo userinfo){
ResponseEntity response = null;
try {
userinfoService.save(userinfo);
response = new ResponseEntity<Void>(HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
}
return response;
}
@RequestMapping(value = "/userinfo/{id}", method = RequestMethod.GET )
public ResponseEntity get(@PathVariable("id") Long id) {
ResponseEntity response = null;
Userinfo userinfo = userinfoService.get(id);
response = new ResponseEntity<Userinfo>(userinfo, HttpStatus.OK);
return response;
}
@RequestMapping(value = "/userinfo/{id}", method = RequestMethod.DELETE )
@ResponseStatus(HttpStatus.OK)
public void delete(@PathVariable("id") Long id) {
userinfoService.delete(id);
}
}
應該很好理解,也試了幾種回覆的寫法,看自己喜好寫吧
Service
package com.sam.modules.service;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.sam.modules.dao.UserinfoMapper;
import com.sam.modules.entity.Userinfo;
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class UserinfoService extends BaseService<Userinfo, Long>{
@Autowired
private UserinfoMapper userinfoMapper;
@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
public int save(Userinfo record) {
record.setTimecreate(new Date(System.currentTimeMillis()));
return userinfoMapper.insert(record);
}
@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
public int updateSelective(Userinfo record) {
return userinfoMapper.updateByPrimaryKeySelective(record);
}
@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
public int updateByReplace(Userinfo record) {
return userinfoMapper.updateByPrimaryKey(record);
}
public Userinfo get(Long serno) {
return userinfoMapper.selectByPrimaryKey(serno);
}
@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
public int delete(Long serno) {
return userinfoMapper.deleteByPrimaryKey(serno);
}
}
這邊就需要對每個方法設定Transactional控制方式,也還好就兩種不要複製錯了就好
打包
在Gradle中增加了 spring-boot-gradle-plugin
所以你只要執行 assemble 就會把所有 Library 包含你的程式打包成一個 Jar 檔
p.s.這邊不能自己用Gradle zip打包,會得到spring boot 1.0版= =.....
執行
在eclipse中是執行 Application.java
命令方式
java -jar springboot-all-1.0.jar
參數檔預設會尋找 application.yaml 或是 application.properties
在程式中就可以透過 @Value("${version}") 來取得變數
這邊設定了多個profiles為了對應不同環境
透過命令列執行可以指定運行哪個設定
java -jar springboot-all-1.0.jar --spring.profiles.active=production
java -jar springboot-all-1.0.jar --spring.profiles.active=docker --dbip=192.168.0.1
version: v0.1.0
server:
port: 9001
spring:
profiles.active: dev
---
spring:
application.name: NtAuth
profiles: dev
datasource:
url: jdbc:postgresql://127.0.0.1:5432/nt?charset=UTF8
username: root
password: abcdEF123456
driverClassName: org.postgresql.Driver
---
spring:
profiles: production
datasource:
url: jdbc:mysql://production.com:3306/productdb
username: db_user
password: db_password
driverClassName: com.mysql.jdbc.Driver
---
spring:
profiles: docker
datasource:
url: jdbc:postgresql://${dbip}:5432/nt?charset=UTF8$
username: ${datasource.username}
password: ${datasource.password}
driverClassName: org.postgresql.Driver
但是 Spring Boot 預設 yaml 的參數檔較優先,如果兩個檔都存在的話,有效的會是 yaml
Spring Boot 所提供的配置優先級順序比較複雜。按照優先級從高到低的順序,具體的列表如下
1. Command line arguments.
2. JNDI attributes from java:comp/env.
3. Java System properties (System.getProperties()).
4. OS environment variables.
5. A RandomValuePropertySource that only has properties in random.*.
6. Profile-specific application properties outside of your packaged jar (application-{profile}.properties and YAML variants)
7. Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants)
8. Application properties outside of your packaged jar (application.properties and YAML variants).
9. Application properties packaged inside your jar (application.properties and YAML variants).
10. @PropertySource annotations on your @Configuration classes.
11. Default properties (specified using SpringApplication.setDefaultProperties).
或是你可以在命令列指定如下
java -jar springboot-all-1.0.jar --spring.config.location=./application.properties
Spring Boot其他項目功能
名稱 | 說明 |
---|---|
spring-boot-starter | 核心 POM,包含自動配置支持、日誌庫和對 YAML 配置文件的支持。 |
spring-boot-starter-amqp | 通過 spring-rabbit 支持 AMQP。 |
spring-boot-starter-aop | 包含 spring-aop 和 AspectJ 來支持面向切麵編程(AOP)。 |
spring-boot-starter-batch | 支持 Spring Batch,包含 HSQLDB。 |
spring-boot-starter-data-jpa | 包含 spring-data-jpa、spring-orm 和 Hibernate 來支持 JPA。 |
spring-boot-starter-data-mongodb | 包含 spring-data-mongodb 來支持 MongoDB。 |
spring-boot-starter-data-rest | 通過 spring-data-rest-webmvc 支持以 REST 方式暴露 Spring Data 倉庫。 |
spring-boot-starter-jdbc | 支持使用 JDBC 訪問數據庫。 |
spring-boot-starter-security | 包含 spring-security。 |
spring-boot-starter-test | 包含常用的測試所需的依賴,如 JUnit、Hamcrest、Mockito 和 spring-test 等。 |
spring-boot-starter-velocity | 支持使用 Velocity 作為模板引擎。 |
spring-boot-starter-web | 支持 Web 應用開發,包含 Tomcat 和 spring-mvc。 |
spring-boot-starter-websocket | 支持使用 Tomcat 開發 WebSocket 應用。 |
spring-boot-starter-ws | 支持 Spring Web Services。 |
spring-boot-starter-actuator | 添加適用於生產環境的功能,如性能指標和監測等功能。 |
spring-boot-starter-remote-shell | 添加遠程 SSH 支持。 |
spring-boot-starter-jetty | 使用 Jetty 而不是默認的 Tomcat 作為應用服務器。 |
spring-boot-starter-log4j | 添加 Log4j 的支持。 |
spring-boot-starter-logging | 使用 Spring Boot 默認的日誌框架 Logback。 |
spring-boot-starter-tomcat | 使用 Spring Boot 默認的 Tomcat 作為應用服務器。 |
完整程式碼GitHub