Accessing-data-from-mysql-purely-by-mybatis(新手向)
java版本:1.8
前言
之前做了Maven+ SpringMVC+ MyBatis +Druid+MySql:http://www.reibang.com/p/4b285b5b34f8瞻赶。這次不用spring,甩脫Spring IOC,來(lái)一波與Mybatis與Mysql純純的激情碰撞个粱。我的項(xiàng)目基礎(chǔ)時(shí)根據(jù)網(wǎng)上的改編的:https://www.cnblogs.com/moy25/p/8455252.html当悔,我多做了一層對(duì)增刪改查的封裝弟劲,畢竟不是僅僅做個(gè)自己玩的demo。
基本概念
Mysql
是個(gè)數(shù)據(jù)庫(kù)唉地,主要有庫(kù),庫(kù)下由表組成传透。我們能對(duì)數(shù)據(jù)庫(kù)與表做增上改查耘沼。
Mybatis
Mybatis官網(wǎng):https://mybatis.org/mybatis-3/
是個(gè)java對(duì)象與mysql打交道的工具。我重點(diǎn)講一下朱盐。每次要與Mysql數(shù)據(jù)庫(kù)會(huì)話時(shí)群嗤,我們要用SqlSessionFactory里的openSession方法創(chuàng)建Sqlsession。構(gòu)建一個(gè)SqlSessionFactory兵琳,我們需要一個(gè)SqlSessionFactoryBuilder狂秘。而 SqlSessionFactoryBuilder 則可以從 XML 配置文件或一個(gè)預(yù)先配置的 Configuration 實(shí)例來(lái)構(gòu)建出 SqlSessionFactory 實(shí)例。
制作過(guò)程
文件結(jié)構(gòu)
你需要的依賴包:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
</dependencies>
其中躯肌,第一個(gè)依賴是用來(lái)做單元測(cè)試的者春,第二三個(gè)顧名思義。
你需要的Mybatis配置
由上文所述清女,我們可以先用 XML 配置文件來(lái)配置SqlSessionFactoryBuilder 钱烟。
我的 XML 配置文件
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPEconfiguration
PUBLIC"-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<!--項(xiàng)目實(shí)體類對(duì)應(yīng)的包名-->
<packagename="mybatis.mysql.without.spring.entity"/>
</typeAliases>
<!--myql數(shù)據(jù)庫(kù)連接信息-->
<environmentsdefault="development">
<environmentid="development">
<transactionManagertype="JDBC"/>
<dataSourcetype="POOLED">
? ? ? ? ? ? <!--驅(qū)動(dòng)器-->
<propertyname="driver"value="com.mysql.jdbc.Driver"/>
? ? ? ? ? ? <!--myql的url地址,db_example為你創(chuàng)建的庫(kù)名-->
? ? ? ? ? ? <propertyname="url"value="jdbc:mysql://127.0.0.1/db_example?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false"/>
? ? ? ? ? ? <!--myql數(shù)據(jù)庫(kù)用戶賬號(hào)-->
<propertyname="username"value="root"/>
? ? ? ? ? ? <!--myql數(shù)據(jù)庫(kù)用戶密碼-->
<propertyname="password"value="root@hiekn"/>
</dataSource>
</environment>
</environments>
<!--配置實(shí)體映射xml路徑-->
<mappers>
<mapperresource="mapper/Test.xml"/>
</mappers>
</configuration>
當(dāng)然,還有很多可以在 XML 文件中配置的選項(xiàng)拴袭,上面的示例僅羅列了最關(guān)鍵的部分读第。 注意 XML 頭部的聲明,它用來(lái)驗(yàn)證 XML 文檔的正確性稻扬。environment 元素體中包含了事務(wù)管理和連接池的配置卦方。mappers 元素則包含了一組映射器(mapper),這些映射器的 XML 映射文件包含了 SQL 代碼和映射定義信息泰佳。
我們要用它放進(jìn)SqlSessionFactoryBuilder 來(lái)配置SqlSessionFactory盼砍。
try{
Readerreader=Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory=newSqlSessionFactoryBuilder().build(reader);
}catch(IOExceptione) {
thrownewRuntimeException(e);
}
既然有了 SqlSessionFactory,顧名思義逝她,我們可以從中獲得 SqlSession 的實(shí)例浇坐。我把 SqlSession 的實(shí)例放在一個(gè)thread中。
publicstaticfinalSqlSessionFactorysqlSessionFactory;
publicstaticfinalThreadLocal<SqlSession>sessionThread=newThreadLocal();
publicstaticSqlSessiongetCurrentSqlSession() {
SqlSessionsqlSession=sessionThread.get();
if(Objects.isNull(sqlSession)) {
sqlSession=sqlSessionFactory.openSession();
sessionThread.set(sqlSession);
?? }
returnsqlSession;
}
可見(jiàn)黔宛,如果thread中沒(méi)有sql映射近刘,那么就從sqlSessionFactory中創(chuàng)建Sqlsession。
與Mysql數(shù)據(jù)庫(kù)會(huì)話結(jié)束時(shí)臀晃,也要關(guān)閉sql映射觉渴,清空thread
SqlSessionsqlSession=sessionThread.get();
if(Objects.nonNull(sqlSession)) {
sqlSession.close();
}
sessionThread.set(null);
這些放在一個(gè)工具類就行了。
Mybatis3Utils
packagemybatis.mysql.without.spring.utils;
importorg.apache.ibatis.io.Resources;
importorg.apache.ibatis.session.SqlSession;
importorg.apache.ibatis.session.SqlSessionFactory;
importorg.apache.ibatis.session.SqlSessionFactoryBuilder;
importjava.io.IOException;
importjava.io.Reader;
importjava.util.Objects;
publicabstractclassMybatis3Utils{
publicstaticfinalSqlSessionFactorysqlSessionFactory;
publicstaticfinalThreadLocal<SqlSession>sessionThread=newThreadLocal();
static{
try{
Readerreader=Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory=newSqlSessionFactoryBuilder().build(reader);
}catch(IOExceptione) {
thrownewRuntimeException(e);
? ? ?? }
?? }
publicstaticSqlSessiongetCurrentSqlSession() {
SqlSessionsqlSession=sessionThread.get();
if(Objects.isNull(sqlSession)) {
sqlSession=sqlSessionFactory.openSession();
sessionThread.set(sqlSession);
? ? ?? }
returnsqlSession;
?? }
publicstaticvoidcloseCurrentSession() {
SqlSessionsqlSession=sessionThread.get();
if(Objects.nonNull(sqlSession)) {
sqlSession.close();
? ? ?? }
sessionThread.set(null);
?? }
}
SqlSession 提供了在數(shù)據(jù)庫(kù)執(zhí)行 SQL 命令所需的所有方法徽惋。你可以通過(guò) SqlSession 實(shí)例來(lái)直接執(zhí)行已映射的 SQL 語(yǔ)句案淋。
Test.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mappernamespace="mybatis.mysql.without.spring.mapper.TestMapper">
<selectid="listAll"resultType="TestEntity">
SELECTid,
create_timecreateTime,
modify_timemodifyTime,
contentcontent
FROMt_test
</select>
<selectid="count"resultType="int">
SELECTcount(*)FROMt_test
</select>
<selectid="get"parameterType="int"resultType="TestEntity">
SELECTid,create_timeascreateTime,modify_timeasmodifyTime,content
FROMt_testWHEREid=#{id}
</select>
<insertid="insert"parameterType="TestEntity"useGeneratedKeys="true"
keyProperty="id">
INSERTINTOt_test(create_time,modify_time,content)
VALUES(#{createTime},#{modifyTime},#{content})
</insert>
<updateid="update"parameterType="mybatis.mysql.without.spring.entity.TestEntity">
UPDATEt_testSETmodify_time=#{modifyTime},content=#{content}WHEREid=#{id}
</update>
<deleteid="delete"parameterType="int">
DELETEFROMt_testWHEREid=#{id}
</delete>
</mapper>
namespace命名規(guī)則
命名解析:為了減少輸入量,MyBatis 對(duì)所有具有名稱的配置元素(包括語(yǔ)句险绘,結(jié)果映射踢京,緩存等)使用了如下的命名解析規(guī)則。
全限定名(比如 “mybatis.mysql.without.spring.mapper.TestMapper“)將被直接用于查找及使用宦棺。
短名稱(比如 “selectAllThings”)如果全局唯一也可以作為一個(gè)單獨(dú)的引用瓣距。 如果不唯一,有兩個(gè)或兩個(gè)以上的相同名稱(比如 “com.foo.selectAllThings” 和 “com.bar.selectAllThings”)代咸,那么使用時(shí)就會(huì)產(chǎn)生“短名稱不唯一”的錯(cuò)誤蹈丸,這種情況下就必須使用全限定名。
至于那些Mybatis映射語(yǔ)句侣背,官網(wǎng)上有白华。https://mybatis.org/mybatis-3/zh/getting-started.html
我要映射的方法存于TestMapper
TestMapper
publicinterfaceTestMapper{
List<TestEntity>listAll();
TestEntityget(Serializableid);
intinsert(TestEntitytestEntity);
intupdate(TestEntitytestEntity);
intdelete(Serializableid);
intcount();
}
我的Service
publicclassService{
privateSqlSessionsqlSession;
privateTestMappertestMapper;
publicService(){
SqlSessionsqlSession=Mybatis3Utils.getCurrentSqlSession();
TestMappertestMapper=sqlSession.getMapper(TestMapper.class);
this.sqlSession=sqlSession;
this.testMapper=testMapper;
?? }
publicvoidinsert(TestEntitytestEntity){
testMapper.insert(testEntity);
sqlSession.commit();//提交事務(wù),是一把鎖贩耐,若其他人也要提交,就會(huì)把它鎖住厦取,當(dāng)然也有權(quán)限區(qū)分
Mybatis3Utils.closeCurrentSession();
?? }
publicvoidlistAll() {
List<TestEntity>list=testMapper.listAll();
System.out.println(Arrays.toString(list.toArray()));
sqlSession.commit();
Mybatis3Utils.closeCurrentSession();
?? }
publicvoidget(Integerid) {
System.out.println(testMapper.get(id));
sqlSession.commit();
Mybatis3Utils.closeCurrentSession();
?? }
publicvoidupdate(Integerid,TestEntitytestEntity) {
testEntity.setId(id);
testMapper.update(testEntity);
sqlSession.commit();
Mybatis3Utils.closeCurrentSession();
?? }
publicvoiddelete(Integerid) {
testMapper.delete(id);
sqlSession.commit();
Mybatis3Utils.closeCurrentSession();
?? }
publicvoidcount(){
System.out.println(testMapper.count());
sqlSession.commit();
Mybatis3Utils.closeCurrentSession();
?? }
}
可見(jiàn)潮太,只有調(diào)用Service中的方法,我才要生成一個(gè)Sql映射。等我方法完成了铡买,我再把這個(gè)Sql映射銷毀更鲁。
sqlSession.commit();//提交事務(wù),是一把鎖奇钞,若其他人也要提交澡为,就會(huì)把它鎖住,當(dāng)然也有權(quán)限區(qū)分
差點(diǎn)忘記我的實(shí)體類了
TestEntity
publicclassTestEntity{
privateIntegerid;
privateDatecreateTime;
privateDatemodifyTime;
privateStringcontent;
publicIntegergetId() {
returnid;
?? }
publicvoidsetId(Integerid) {
this.id=id;
?? }
publicDategetCreateTime() {
returncreateTime;
?? }
publicvoidsetCreateTime(DatecreateTime) {
this.createTime=createTime;
?? }
publicDategetModifyTime() {
returnmodifyTime;
?? }
publicvoidsetModifyTime(DatemodifyTime) {
this.modifyTime=modifyTime;
?? }
publicStringgetContent() {
returncontent;
?? }
publicvoidsetContent(Stringcontent) {
this.content=content;
?? }
@Override
publicStringtoString() {
return"TestEntity{"+
"id="+id+
", createTime="+createTime+
", modifyTime="+modifyTime+
", content='"+content+'\''+
'}';
?? }
}
這樣景埃,你既可以用單元測(cè)試測(cè)試接口媒至,也能在啟動(dòng)類上調(diào)用,其他人也能調(diào)用了谷徙。
根據(jù)官網(wǎng)上對(duì)于作用域與生命周期的闡釋拒啰,還是用注解構(gòu)建Sql映射保險(xiǎn)。
作用域(Scope)和生命周期
理解我們之前討論過(guò)的不同作用域和生命周期類別是至關(guān)重要的完慧,因?yàn)殄e(cuò)誤的使用會(huì)導(dǎo)致非常嚴(yán)重的并發(fā)問(wèn)題谋旦。
提示 對(duì)象生命周期和依賴注入框架
依賴注入框架可以創(chuàng)建線程安全的、基于事務(wù)的 SqlSession 和映射器屈尼,并將它們直接注入到你的 bean 中册着,因此可以直接忽略它們的生命周期。 如果對(duì)如何通過(guò)依賴注入框架使用 MyBatis 感興趣脾歧,可以研究一下 MyBatis-Spring 或 MyBatis-Guice 兩個(gè)子項(xiàng)目甲捏。
SqlSessionFactoryBuilder
這個(gè)類可以被實(shí)例化、使用和丟棄涨椒,一旦創(chuàng)建了 SqlSessionFactory摊鸡,就不再需要它了。 因此 SqlSessionFactoryBuilder 實(shí)例的最佳作用域是方法作用域(也就是局部方法變量)蚕冬。 你可以重用 SqlSessionFactoryBuilder 來(lái)創(chuàng)建多個(gè) SqlSessionFactory 實(shí)例免猾,但最好還是不要一直保留著它,以保證所有的 XML 解析資源可以被釋放給更重要的事情囤热。
SqlSessionFactory
SqlSessionFactory 一旦被創(chuàng)建就應(yīng)該在應(yīng)用的運(yùn)行期間一直存在猎提,沒(méi)有任何理由丟棄它或重新創(chuàng)建另一個(gè)實(shí)例。 使用 SqlSessionFactory 的最佳實(shí)踐是在應(yīng)用運(yùn)行期間不要重復(fù)創(chuàng)建多次旁蔼,多次重建 SqlSessionFactory 被視為一種代碼“壞習(xí)慣”锨苏。因此 SqlSessionFactory 的最佳作用域是應(yīng)用作用域。 有很多方法可以做到棺聊,最簡(jiǎn)單的就是使用單例模式或者靜態(tài)單例模式伞租。
SqlSession
每個(gè)線程都應(yīng)該有它自己的 SqlSession 實(shí)例。SqlSession 的實(shí)例不是線程安全的限佩,因此是不能被共享的葵诈,所以它的最佳的作用域是請(qǐng)求或方法作用域裸弦。 絕對(duì)不能將 SqlSession 實(shí)例的引用放在一個(gè)類的靜態(tài)域,甚至一個(gè)類的實(shí)例變量也不行作喘。 也絕不能將 SqlSession 實(shí)例的引用放在任何類型的托管作用域中理疙,比如 Servlet 框架中的 HttpSession。 如果你現(xiàn)在正在使用一種 Web 框架泞坦,考慮將 SqlSession 放在一個(gè)和 HTTP 請(qǐng)求相似的作用域中窖贤。 換句話說(shuō),每次收到 HTTP 請(qǐng)求贰锁,就可以打開(kāi)一個(gè) SqlSession赃梧,返回一個(gè)響應(yīng)后,就關(guān)閉它李根。 這個(gè)關(guān)閉操作很重要槽奕,為了確保每次都能執(zhí)行關(guān)閉操作,你應(yīng)該把這個(gè)關(guān)閉操作放到 finally 塊中房轿。 下面的示例就是一個(gè)確保 SqlSession 關(guān)閉的標(biāo)準(zhǔn)模式:
try(SqlSessionsession=sqlSessionFactory.openSession()) {
// 你的應(yīng)用邏輯代碼
}
在所有代碼中都遵循這種使用模式粤攒,可以保證所有數(shù)據(jù)庫(kù)資源都能被正確地關(guān)閉。
映射器實(shí)例
映射器是一些綁定映射語(yǔ)句的接口囱持。映射器接口的實(shí)例是從 SqlSession 中獲得的夯接。雖然從技術(shù)層面上來(lái)講,任何映射器實(shí)例的最大作用域與請(qǐng)求它們的 SqlSession 相同纷妆。但方法作用域才是映射器實(shí)例的最合適的作用域盔几。 也就是說(shuō),映射器實(shí)例應(yīng)該在調(diào)用它們的方法中被獲取掩幢,使用完畢之后即可丟棄逊拍。 映射器實(shí)例并不需要被顯式地關(guān)閉。盡管在整個(gè)請(qǐng)求作用域保留映射器實(shí)例不會(huì)有什么問(wèn)題际邻,但是你很快會(huì)發(fā)現(xiàn)芯丧,在這個(gè)作用域上管理太多像 SqlSession 的資源會(huì)讓你忙不過(guò)來(lái)。 因此世曾,最好將映射器放在方法作用域內(nèi)缨恒。就像下面的例子一樣:
try(SqlSessionsession=sqlSessionFactory.openSession()) {
BlogMappermapper=session.getMapper(BlogMapper.class);
// 你的應(yīng)用邏輯代碼
}
java注解實(shí)體映射
雖說(shuō)由于 Java 注解的一些限制以及某些 MyBatis 映射的復(fù)雜性,要使用大多數(shù)高級(jí)映射(比如:嵌套聯(lián)合映射)轮听,仍然需要使用 XML 配置骗露。但是一般來(lái)說(shuō)我們不會(huì)太復(fù)雜,因?yàn)橐紤]系統(tǒng)性能不能太復(fù)雜血巍,而它又簡(jiǎn)單又可以忽略他們的生命周期萧锉,我們就來(lái)展示一下用注解的方法。
我就舉一個(gè)例子述寡。
其實(shí)很簡(jiǎn)單驹暑,只要改幾處:
Mybatis配置中要改為直接用TestMapper類作為地址玫恳。
<mappers>
<mapperclass="mybatis.mysql.without.spring.mapper.TestMapper"/>
</mappers>
加上幾個(gè)Results和Result類辨赐。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public@interfaceResults{
booleanid()defaultfalse;
Result[]value()default{};
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public@interfaceResult{
booleanid()defaultfalse;
Stringcolumn()default"";
Stringproperty()default"";
}
TestMapper改為注解形式优俘。
@Select("select * from t_test")
@Options(useGeneratedKeys=true,keyProperty="id")
@Results(value={
@Result(id=true,column="id",property="id"),
@Result(id=true,column="create_time",property="createTime"),
@Result(id=true,column="modify_time",property="modifyTime"),
@Result(id=true,column="content",property="content")
}
)
其中,column是在表中的掀序,property是在對(duì)象中的帆焕。