springboot和mybatis結合
springboot和mybatis結合
依賴和資料來源配置
springboot依賴了spring4,需要依賴mybatis-spring,最新版本是1.2.2。
資料來源相關的依賴:
<!-- datasource -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-java6</artifactId>
<version>${HikariCP.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
前兩個是資料來源的依賴,包括HikariCP和mysql驅動。後面兩個是mybatis依賴,包括mybatis本身和mybatis-spring模組。
有了這些依賴之後,就可以通過spring4的配置類,對mybatis資料來源等進行配置。
@Configuration
@PropertySource("classpath:datasource.properties")
@MapperScan(basePackages="xxx.repository", sqlSessionFactoryRef = "sqlSessionFactory")
public class DatasourceConfig {
@Autowired
private Environment env;
@Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName("com.mysql.jdbc.Driver");
config.setAutoCommit(false);
config.setJdbcUrl(env.getProperty("xxx.db.url"));
config.setUsername(env.getProperty("xxx.db.username"));
config.setPassword(env.getProperty("xxx.db.password"));
return new HikariDataSource(config);
}
@Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setTypeAliasesPackage("xxx.mybatis");
return sessionFactory.getObject();
}
}
首先引入配置檔案,並且注入到env物件中。env類似System的properties物件,封裝了配置檔案中的key value。
然後通過MapperScan註解定義mapper介面包路徑。這裡同時定義了sqlSessionFactoryRef,是因為需要用到多資料來源,
防止spring無法注入,後面會提到。
之後程式碼就可以開始定義輸出的bean。一個是datasource,直接初始化一個Hikari的資料來源,springboot提供了builder類,
但是檢視原始碼和api之後,DataSourceBuilder無法配置autocommit屬性。
再下面是事務管理,需要通過建構函式注入dataSource。最後一個是mybatis的sqlSessionFactory,主要也是注入一個資料來源。
mapper(DAO)實現
dao實現和原先的ibatis差不多,但是mybatis可以通過註解的形式直接生成動態sql。既然springboot用了程式碼來取代xml,mybatis
中也同樣去掉了xml。
插入
插入操作需要注意兩個地方,一個是如何返回插入之後的主鍵(mysql),一個是如何使用資料型別的handler。
首先看程式碼:
@Insert("INSERT INTO aegis_cron_timer " +
"(id, gmt_create, gmt_modified, name, expression, event_class_name, description, last_trigger_time, status, parameter) " +
"VALUES (NULL, now(), now(), #{name:VARCHAR}, #{expression:VARCHAR}, " +
"#{eventClassName:VARCHAR}, #{description:VARCHAR}, now(), #{status:VARCHAR}, " +
"#{parameter,typeHandler=com.alibaba.aegis.seawater.cron.service.dao.mybatis.MapToJsonTypeHandler})")
@SelectKey(before = false, statement = "SELECT LAST_INSERT_ID()", keyProperty = "id", resultType = java.lang.Long.class)
public Long insertCronTimer(CronTimer cronTimer);
針對mysql,可以通過SelectKey這個註解,設定插入後主鍵的返回。由於mysql是自增主鍵,所以設定為插入後執行,定義返回的型別為long
(資料庫中定義了bigint)。
另外,這裡有個欄位需要從map序列化成json字串,作為varchar型別存放到資料庫中。在插入的sql中,可以直接在變數後面定義typeHandler,
值是對應handler的完整類名。
更新
更新操作比較簡單,直接使用Update註解即可。和插入類似,如果需要指定type handler,直接在欄位後面增加引數即可。更新函式可以返回一個int值,
表示本次更新的行數。
查詢
查詢通過Select註解完成,mybatis可以直接通過欄位名字和查詢結果的java bean之間做自動關聯。如果名字不匹配,有兩種方式,一種是通過sql中
增加AS關鍵字轉成java bean中的欄位名,一種是通過@Result註解指定二者的對映關係。
@Select("SELECT name, expression, event_class_name AS eventClassName, description, status, parameter " +
"FROM aegis_cron_timer " +
"WHERE status = `ENABLE`")
@Results({
@Result(column = "parameter", jdbcType = JdbcType.VARCHAR, property = "parameter", typeHandler = MapToJsonTypeHandler.class)
})
public List<CronTimer> listAllAvailableCronTimer();
這裡通過Result註解配置了type handler,特別注意Result註解必須在Results註解中,不然不會生效。
自定義type handler
前文已經提到了如何在插入、更新、查詢語句中使用type handler,type handler實現也比較簡單。mybatis自帶的type handler都是通過extends
BaseTypeHandler來實現的,但例子中直接實現了TypeHandler介面:
@MappedTypes(Map.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MapToJsonTypeHandler implements TypeHandler<Map<String, Object>> {
@Override
public void setParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, JSON.toJSONString(parameter));
}
@Override
public Map<String, Object> getResult(ResultSet rs, String columnName) throws SQLException {
String value = rs.getString(columnName);
return jsonToMap(value);
}
@Override
public Map<String, Object> getResult(ResultSet rs, int columnIndex) throws SQLException {
String value = rs.getString(columnIndex);
return jsonToMap(value);
}
@Override
public Map<String, Object> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String value = cs.getString(columnIndex);
return jsonToMap(value);
}
private Map<String,Object> jsonToMap(String value) {
if (StringUtils.isBlank(value)) {
return Collections.emptyMap();
} else {
return JSON.parseObject(value, new TypeReference<Map<String, Object>>() {
});
}
}
}
實現比較簡單,序列化的時候直接通過fastjson將map物件轉成json string,放到PreparedStatement中。反序列化的時候返回來轉成Map即可。
多資料來源實現
由於專案需要從老的資料庫遷移到新的資料庫,所以需要兩個資料來源,在設定多資料來源的時候也踩了很多坑。
另一個資料來源配置類:
@Configuration
@PropertySource("classpath:amon-datasource.properties")
@MapperScan(basePackages="com.alibaba.aegis.seawater.cron.migrate.repository",
sqlSessionFactoryRef = "amonSqlSessionFactory", sqlSessionTemplateRef = "amonSqlSessionTemplate")
public class AmonDataSourceConfig {
@Autowired
private Environment env;
@Bean(name = "amonDataSource")
public DataSource amonDataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName("com.mysql.jdbc.Driver");
config.setAutoCommit(true);
config.setJdbcUrl(env.getProperty("amon.db.url"));
config.setUsername(env.getProperty("amon.db.username"));
config.setPassword(env.getProperty("amon.db.password"));
return new HikariDataSource(config);
}
@Bean(name = "amonTransactionManager")
public DataSourceTransactionManager amonTransactionManager(@Qualifier("amonDataSource")DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "amonSqlSessionFactory")
public SqlSessionFactory amonSqlSessionFactory(@Qualifier("amonDataSource")DataSource dataSource) throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
return sessionFactory.getObject();
}
@Bean(name = "amonSqlSessionTemplate")
public SqlSessionTemplate amonSqlSessionTemplate(@Qualifier("amonSqlSessionFactory")SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
這裡也定義了一個配置檔案,需要注意的是不要和前面一個的key相同,不然會覆蓋的。定義bean的時候需要設定下name,或者函式名字改了也行。
需要定義的bean和之前的一樣,特別注意MapperScan註解需要修改sqlSessionFactoryRef或者sqlSessionTemplateRef。這裡兩個都改了但是啟動的時候會
提示:
Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.
這邊定義了bean之後,直接使用就沒有問題了。唯一需要特別注意的是@Transactional註解,由於定義了兩個transactionManager,
無法通過型別來注入事務管理器了,需要註解中特別指定。比如使用前面定義的資料來源的事物管理器,需要改成:
@Transactional("transactionManager")
這樣spring可以通過名字注入bean。
DAO測試
為了方便測試,對應測試類中,重新覆蓋了dataSource,採用h2這種記憶體資料庫,解決單元測試資料干擾。
@Configuration
@MapperScan(basePackages="com.alibaba.aegis.seawater.cron.repository")
public class TestDatasourceConfig extends DatasourceConfig {
@Autowired
private Environment env;
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.setName("cron")
.addScript("h2.sql")
.build();
}
}
這裡直接通過springboot提供的EmbeddedDatabaseBuilder來建立一個h2的資料庫,並新增初始化資料庫schema的sql檔案。
這裡需要注意的是,如果這個sql檔案直接叫schema.sql,之前mysql資料來源在執行的時候也會去執行,因此這裡沒有使用預設的名字。
其他坑
在springboot注入properties檔案中配置的時候,還遇到一個噁心的問題,除了PropertySource註解指定的properties檔案之外,
spring還會預設帶上jvm變數、系統環境變數。剛開始直接把資料庫使用者名稱欄位的key寫成了username,結果由於測試伺服器上使用了sudo
命令,sudo在切換使用者的同時設定了USERNAME這個環境變數標識原始執行使用者,導致springboot一直在注入這個值,除錯了很久。
該文章來自於阿里巴巴技術協會(ATA)
作者:金靈傑
相關文章
- 關於SpringBoot結合mybatis後遇到的坑Spring BootMyBatis
- Springboot 多資料來源配置,結合tk-mybatisSpring BootMyBatis
- SpringBoot--整合Mybatis和RedisSpring BootMyBatisRedis
- SpringBoot:結合 SpringBoot 與 Grails 3Spring BootAI
- SpringBoot與mongodb的結合Spring BootMongoDB
- flutter+Springboot的結合FlutterSpring Boot
- Mybatis的初始化和結合Spring Framework後初始化的MyBatisSpringFramework
- 10.Mybatis在springboot中的整合總結MyBatisSpring Boot
- springBoot 整合 mybatisSpring BootMyBatis
- Springboot整合MybatisSpring BootMyBatis
- springboot+mybatisSpring BootMyBatis
- Mybatis一級快取和結合Spring Framework後失效的原始碼探究MyBatis快取SpringFramework原始碼
- springboot結合Redis實現工具類Spring BootRedis
- 【SpringBoot】結合Redis實現快取Spring BootRedis快取
- SpringBoot--SpringBoot 讀取Properties檔案(結合JDBC)Spring BootJDBC
- springboot+mybatis整合Spring BootMyBatis
- SpringBoot 中的 MyBatisSpring BootMyBatis
- SpringBoot | 3.2 整合MyBatisSpring BootMyBatis
- Springboot+mybatis 整合Spring BootMyBatis
- 記錄Mybatis和Ehcache結合時啟用延遲載入的一個坑MyBatis
- 多資料來源結合mybatis-plus的使用MyBatis
- 【springboot】【java】【MySQL】【mybatis】【db】mybatis初體驗Spring BootJavaMySqlMyBatis
- Mybatis的初始化和結合Spring Framework後初始化的原始碼探究MyBatisSpringFramework原始碼
- SpringBoot_2_integrate_MyBatisSpring BootMyBatis
- springBoot 整合 mybatis+OracleSpring BootMyBatisOracle
- SpringBoot整合Mybatis-PlusSpring BootMyBatis
- springboot專案整合mybatisSpring BootMyBatis
- SpringBoot使用Mybatis-PageHelperSpring BootMyBatis
- springboot-整合mybatis,securitySpring BootMyBatis
- SpringBoot與單元測試JUnit的結合Spring Boot
- SpringBoot基礎教程(十六)——與docker的結合Spring BootDocker
- springboot 結合jackson資料脫敏Spring Boot
- Redis 結合 Docker 搭建叢集,並整合SpringBootRedisDockerSpring Boot
- MyBatis 進階,MyBatis-Plus!(基於 Springboot 演示)MyBatisSpring Boot
- Nginx和Perl的結合Nginx
- Nginx和php的結合NginxPHP
- lucene和NoSQL的結合SQL
- 企業 SpringBoot 教程(六)springboot整合mybatisSpring BootMyBatis