mysql异步查询 java_基于 mysql 异步驱动的非阻塞 Mybatis
雖然 spring5 也推出了 WebFlux 這一套異步技術(shù)棧,這種極大提升吞吐的玩法在 node 里玩的風(fēng)生水起,但 java 世界里異步依舊不是主流,Vertx 倒是做了不少對(duì)異步的支持,但是其對(duì)于數(shù)據(jù)訪問(wèn)層的封裝依舊還是挺精簡(jiǎn)的,傳統(tǒng)的 javaer 還是受不了這種沒(méi)有對(duì)象映射的工具庫(kù),于是我嘗試將 Mybatis 移植到了異步驅(qū)動(dòng)上,讓數(shù)據(jù)訪問(wèn)層的工作變得更簡(jiǎn)單一些。給個(gè)例子:
@Sql(User.class)
public interface CommonMapper {
@Select(columns = "id,age,username")
@OrderBy("id desc")
@Page
@ModelConditions({
@ModelCondition(field = "username", criterion = Criterions.EQUAL),
@ModelCondition(field = "maxAge", column = "age", criterion = Criterions.LESS),
@ModelCondition(field = "minAge", column = "age", criterion = Criterions.GREATER)
})
void query(UserSearch userSearch, DataHandler> handler);
}
上面是 mapper 接口定義,方法的最后一個(gè)參數(shù)因?yàn)楫惒降脑蛩宰兂闪艘粋€(gè)回調(diào),不同的是有很多注解來(lái)表達(dá) sql,看到這些注解應(yīng)該不難猜出 sql 語(yǔ)句吧。如果不喜歡你當(dāng)然可以繼續(xù)使用 mapper.xml 的方式來(lái)寫(xiě) sql。
更多內(nèi)容移步代碼庫(kù)吧~
AsyncDao
asyncDao是一款異步非阻塞模型下的數(shù)據(jù)訪問(wèn)層工具。
MySQL only. 基于MySQL的異步驅(qū)動(dòng)
借鑒了Mybatis的mapping 和 dynamicSQL的內(nèi)容,Mybatiser可以無(wú)縫切換
注解表達(dá)SQL的能力
事務(wù)支持
SpringBoot支持
Mybatis like
使用上與Mybatis幾乎一致,由于異步非阻塞的關(guān)系,數(shù)據(jù)的返回都會(huì)通過(guò)回調(diào)DataHandler來(lái)完成,所以方法定義參數(shù)的最后一個(gè)一定是DataHandler類(lèi)型。由于需要提取方法的參數(shù)名,于是需要加上編譯參數(shù)-parameters,請(qǐng)將它在IDE和maven里配置上。
public interface CommonDao {
void query(User user, DataHandler> handler);
void querySingle(User user, DataHandler handler);
void querySingleMap(User user, DataHandler handler);
void insert(User user,DataHandler handler);
void update(User user,DataHandler handler);
void delete(User user,DataHandler handler);
}
mapper.xml與Mybatis幾乎一致的寫(xiě)法(覆蓋常見(jiàn)標(biāo)簽,一些不常用標(biāo)簽可能不支持,動(dòng)態(tài)SQL建議使用注解SQL功能)
select * from T_User
AND username = #{user.username}
OR age > #{user.age}
order by id desc
insert into T_User
old_address,
created_at,
password,
now_address,
state,
age,
username,
updated_at,
#{user.oldAddress},
#{user.createdAt},
#{user.password},
#{user.nowAddress},
#{user.state},
#{user.age},
#{user.username},
#{user.updatedAt},
update T_User
password=#{user.password},
age=#{user.age},
where id = #{user.id}
注解SQL
在XML里寫(xiě)SQL對(duì)于一些常見(jiàn)SQL實(shí)在是重復(fù)勞動(dòng),so這里允許你利用注解來(lái)表達(dá)SQL,該怎么做呢?
Table與Model關(guān)聯(lián)
@Table(name = "T_User")
public class User {
@Id("id")
private Long id;
//建議全部用包裝類(lèi)型,并注意mysql中字段類(lèi)型與java類(lèi)型的對(duì)應(yīng)關(guān)系,mysql的int不會(huì)自動(dòng)裝換到這里的long
private String username;
private Integer age;
@Column("now_address")
private String nowAddress;
@Column("created_at")
private LocalDateTime createdAt;
//asyncDao 里sql的時(shí)間類(lèi)型都用joda,注意不是JDK8提供的那個(gè),而是第三方包org.joda.time
@Ignore
private String remrk;
@Table記錄數(shù)據(jù)表的名字 @Id記錄主鍵信息 @Column映射了表字段和屬性的關(guān)系,如果表字段和類(lèi)屬性同名,那么可以省略這個(gè)注解 @Ingore忽略這個(gè)類(lèi)屬性,沒(méi)有哪個(gè)表字段與它關(guān)聯(lián)。
定義接口
@Sql(User.class)
public interface CommonDao {
@Select(columns = "id,age,username")
@OrderBy("id desc")
@Page
@ModelConditions({
@ModelCondition(field = "username", criterion = Criterions.EQUAL),
@ModelCondition(field = "maxAge", column = "age", criterion = Criterions.LESS),
@ModelCondition(field = "minAge", column = "age", criterion = Criterions.GREATER)
})
void query(UserSearch userSearch, DataHandler> handler);
@Select(columns = "age,username")
@OrderBy("id desc")
void queryParam(@Condition String username,
@Condition(criterion = Criterions.GREATER) Integer age,
@OffSet int offset,
@Limit int limit,
DataHandler> handler);
@Select(columns = "username,age", sqlMode = SqlMode.COMMON)
void queryList(@Condition(criterion = Criterions.IN, column = "id") int[] ids, DataHandler> handler);
@Insert(useGeneratedKeys = true, keyProperty = "id")
void insert(User user, DataHandler handler);
@Update
@ModelConditions(@ModelCondition(field = "id"))
void update(User user, DataHandler handler);
@Delete
@ModelConditions(@ModelCondition(field = "id"))
void delete(User user, DataHandler handler);
}
看到這些注解你應(yīng)該能猜出來(lái)SQL長(zhǎng)什么樣,接下來(lái)解釋一下這些注解
查詢
@Select(columns = "id,age,username")
@OrderBy("id desc")
@Page
@ModelConditions({
@ModelCondition(field = "username", criterion = Criterions.EQUAL),
@ModelCondition(field = "maxAge", column = "age", criterion = Criterions.LESS),
@ModelCondition(field = "minAge", column = "age", criterion = Criterions.GREATER)
})
void query(UserSearch userSearch, DataHandler> handler);
@Select
columns:默認(rèn) select *可以配置columns("username,age")選擇部分字段;
SqlMode:有兩個(gè)選擇,SqlMode.SELECTIVE 和 SqlMode.COMMON,區(qū)別是selective會(huì)檢查查詢條件的字段是否為null來(lái)實(shí)現(xiàn)動(dòng)態(tài)的查詢,即值為null時(shí)不會(huì)成為查詢條件。并且@Select,@Count,@Update,@Delete都有selective這個(gè)屬性。
@Condition
criterion:查詢條件,=,,in等,具體見(jiàn)Criterions
column:與表字段的對(duì)應(yīng),若與字段名相同可不配置
attach:連接 and,or, 默認(rèn)是and
test:SqlMode為selective下的判斷表達(dá)式,類(lèi)似Mybatis里的test屬性,動(dòng)態(tài)化查詢條件
@Limit,@OffSet為分頁(yè)字段。
方法的參數(shù)不加任何注解一樣會(huì)被當(dāng)做查詢條件,如下面兩個(gè)函數(shù)效果是一樣的:
@Select()
void queryUser(Integer age,DataHandler> handler);
@Select()
void queryUser(@Condition(criterion = Criterions.EQUAL, column = "age") Integer age,DataHandler> handler);
查詢Model
上面的例子在查詢條件比較多時(shí)方法參數(shù)會(huì)比較多,我們可以把查詢條件封裝到一個(gè)類(lèi)里,使用@ModelConditions來(lái)注解查詢條件,注意被@ModelConditions注解的方法只能有兩個(gè)參數(shù),一個(gè)是查詢model,一個(gè)是DataHandler。
@Select
@Page
@ModelConditions({
@ModelCondition(field = "username", criterion = Criterions.EQUAL),
@ModelCondition(field = "minAge", column = "age", criterion = Criterions.GREATER),
@ModelCondition(field = "maxAge", column = "age", criterion = Criterions.LESS),
@ModelCondition(field = "ids", column = "id", criterion = Criterions.IN)
})
void queryUser5(UserSearch userSearch,DataHandler> handler);
@ModelCondition
field:必填,查詢條件中類(lèi)對(duì)應(yīng)的屬性
column:對(duì)應(yīng)的表字段
test:動(dòng)態(tài)SQL的判斷表達(dá)式
@Page只能用在ModelConditions下的查詢,并且方法參數(shù)的那個(gè)類(lèi)應(yīng)該有offset,limit這兩個(gè)屬性,或者 使用@Page(offsetField = "offset",limitField = "limit")指定具體字段
統(tǒng)計(jì)
@Count
void count(DataHandler handler);//返回Long類(lèi)型
插入
@Insert(useGeneratedKeys = true, keyProperty = "id")//返回自增id
void insert(User user, DataHandler handler);
更新
@Update(columns = "username,age")//選擇更新某幾個(gè)列
void update(User user, DataHandler handler);//返回affectedRows
刪除
@Delete
int delete(@Condition(criterion = Criterions.GREATER, column = "age") int min,
@Condition(criterion = Criterions.LESS, column = "age") int max,
DataHandler handler);
@Delete
@ModelConditions(@ModelCondition(field = "id"))
void delete(User user, DataHandler handler);
使用
簡(jiǎn)單的編程使用
AsyncConfig asyncConfig = new AsyncConfig();
PoolConfiguration configuration = new PoolConfiguration("username", "localhost", 3306, "password", "database-name");
asyncConfig.setPoolConfiguration(configuration);
asyncConfig.setMapperPackages("com.tg.async.mapper");//mapper接口
asyncConfig.setXmlLocations("mapper/");//xml目錄,classpath的相對(duì)路徑,不支持絕對(duì)路徑
AsyncDaoFactory asyncDaoFactory = AsyncDaoFactory.build(asyncConfig);
CommonDao commonDao = asyncDaoFactory.getMapper(CommonDao.class);
UserSearch userSearch = new UserSearch();
userSearch.setUsername("ha");
userSearch.setMaxAge(28);
userSearch.setMinAge(8);
userSearch.setLimit(5);
CountDownLatch latch = new CountDownLatch(1);
commonDao.query(user, users -> {
System.out.println(users);
latch.countDown();
});
latch.await();
事務(wù)
Mybatis和Spring體系里有一個(gè)非常好用的@Translactional注解,我們知道事務(wù)本質(zhì)就是依賴connection的rollback等操作,那么一個(gè)事務(wù)下多個(gè)SQL就要共用這一個(gè)connection,如何共享呢?傳統(tǒng)的阻塞體系下ThreadLocal就成了實(shí)現(xiàn)這一點(diǎn)的完美解決方案。那么在異步世界里,要實(shí)現(xiàn)mybatis-spring一樣的上層Api來(lái)完成事務(wù)操作是一件非常困難的事,難點(diǎn)就在于Api太上層,以至于無(wú)法實(shí)現(xiàn)connection共享。于是這里自能退而求其次,使用編程式的方式來(lái)使用事務(wù),抽象出一個(gè)Translaction,具體的mapper通過(guò)translaction.getMapper()來(lái)獲取,這樣通過(guò)同一個(gè)Translaction得到的Mapper都將共用一個(gè)connection。
CountDownLatch latch = new CountDownLatch(1);
AsyncConfig asyncConfig = new AsyncConfig();
PoolConfiguration configuration = new PoolConfiguration("username", "localhost", 3306, "password", "database-name");
asyncConfig.setPoolConfiguration(configuration);
asyncConfig.setMapperPackages("com.tg.async.mapper");
asyncConfig.setXmlLocations("mapper/");
asyncDaoFactory = AsyncDaoFactory.build(asyncConfig);
asyncDaoFactory.startTranslation(res -> {
Translaction translaction = res.result();
System.out.println(translaction);
CommonDao commonDao = translaction.getMapper(CommonDao.class);
User user = new User();
user.setUsername("insert");
user.setPassword("1234");
user.setAge(28);
commonDao.insert(user, id -> {
System.out.println(id);
translaction.rollback(Void -> {
latch.countDown();
});
});
});
latch.await();
SpringBoot
雖然Spring5推出了WebFlux,但異步體系在Spring里依舊不是主流。在異步化改造的過(guò)程中,大部分人也往往會(huì)保留Spring的IOC,而將其他交給Vertx,所以asyncDao對(duì)于Spring的支持就是將Mapper注入IOC容器。
quick start
YAML配置文件:
async:
dao:
mapperLocations: /mapper #xml目錄,classpath的相對(duì)路徑,不支持絕對(duì)路徑
basePackages: com.tg.mapper #mapper所在包
username: username
host: localhost
port: 3306
password: pass
database: database-name
maxTotal: 12
maxIdle: 12
minIdle: 1
maxWaitMillis: 10000
添加@Mapper來(lái)實(shí)現(xiàn)注入
@Mapper
@Sql(User.class)
public interface CommonDao {
@Select(columns = "id,age,username")
@OrderBy("id desc")
@Page(offsetField = "offset", limitField = "limit")
@ModelConditions({
@ModelCondition(field = "username", criterion = Criterions.EQUAL),
@ModelCondition(field = "maxAge", column = "age", criterion = Criterions.LESS),
@ModelCondition(field = "minAge", column = "age", criterion = Criterions.GREATER)
})
void query(UserSearch userSearch, DataHandler> handler);
}
通過(guò)@EnableAsyncDao來(lái)開(kāi)啟支持,簡(jiǎn)單示例:
@SpringBootApplication
@EnableAsyncDao
public class DemoApplication {
public static void main(String[] args){
ApplicationContext applicationContext = SpringApplication.run(DemoApplication.class);
CommonDao commonDao = applicationContext.getBean(CommonDao.class);
UserSearch userSearch = new UserSearch();
userSearch.setUsername("ha");
userSearch.setMaxAge(28);
userSearch.setMinAge(8);
userSearch.setLimit(5);
commonDao.query(userSearch, users -> {
System.out.println("result: " + users);
});
}
}
總結(jié)
以上是生活随笔為你收集整理的mysql异步查询 java_基于 mysql 异步驱动的非阻塞 Mybatis的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 电脑磁盘格式化会怎么样 电脑磁盘格式化的
- 下一篇: java矩形翻转_如何判断一个点在旋转后