Mybatis
Maven依賴:
<!-- https:///artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
持久層
持久化就是將持久狀態(tài)和瞬時(shí)狀態(tài)轉(zhuǎn)化的過程
數(shù)據(jù)庫(JDBC),IO文件持久化
1、第一個(gè)Mybatis程序
1.1、基礎(chǔ)配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-////DTD Config 3.0//EN"
"http:///dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://;ocalhost:3306/smbms?serverTimezone=UTC&userSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="handhand"/>
</dataSource>
</environment>
</environments>
</configuration>
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的實(shí)例。SqlSession 提供了在數(shù)據(jù)庫執(zhí)行 SQL 命令所需的所有方法。你可以通過 SqlSession 實(shí)例來直接執(zhí)行已映射的 SQL 語句。例如:
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
1.2、編寫代碼
public class User {
private int id;
private String name;
private String password;
public User(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public User() {
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
public interface UserDao {
public List<User> getUserList();
}
- 接口實(shí)現(xiàn)類,由原來的UserDaoImpl轉(zhuǎn)變?yōu)镸apper配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-////DTD Mapper 3.0//EN"
"http:///dtd/mybatis-3-mapper.dtd">
<!--命名空間,綁定一個(gè)Dao接口-->
<mapper namespace="com.darker.dao.UserDao">
<select id="getUserList" resultType="com.darker.pojo.User" >
select * from smbms.user
</select>
</mapper>
1.3測試
注意點(diǎn):org.apache.ibatis.binding.BindingException: Type interface com.darker.dao.UserDao is not known to the MapperRegistry.
<!--每一個(gè)Mapper.xml都需要在Mybatis的核心配置文件中注冊-->
<mappers>
<mapper resource="com/darker/dao/UserMapper.xml"/>
</mappers>
public class UserDaoTest {
@Test
public void test() {
//獲得session對象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:getMapper執(zhí)行sql
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
//關(guān)閉SqlSession
sqlSession.close();
}
}
2、CRUD
1、namespace
namespace中的報(bào)名要喝Da/mapper接口的包名一致
2、select
選擇,查詢語句:
-
id:對應(yīng)namespace接口中的方法名
-
resultType:Sql語句執(zhí)行的返回值
-
parameterType:參數(shù)類型
<select id="getUserList" resultType="com.darker.pojo.User">
select *
from user
</select>
3、insert
<insert id="addUser" parameterType="com.darker.pojo.User">
insert into user
values (#{id}, #{name}, #{password});
</insert>
4、update
<update id="updateUser" parameterType="com.darker.pojo.User">
update user
set name = #{name},
password = #{password}
where id = #{id};
</update>
5、delete
<delete id="deleteUser" parameterType="int">
delete
from user
where id =
#{id}
</delete>
6、注意點(diǎn)
增刪改需要sqlSession.commit(); 提交事物。
7、Map
假設(shè)實(shí)體類中的字段過多,考慮使用Map。
@Test
public void addUser2() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> hashMap = new HashMap<String, Object>();
hashMap.put("userid",5);
hashMap.put("username","safasdf");
hashMap.put("userpassword","2341212");
mapper.addUser2(hashMap);
sqlSession.close();
}
<insert id="addUser2" parameterType="map">
insert into user
values (#{userid}, #{username}, #{userpassword});
</insert>
//新增用戶
int addUser2(HashMap<String, Object> map);
8、模糊查詢
在傳參是使用%%
List<User> userList = mapper.getUserLikeList("%測試%");
SQL中寫
select * from user where name like "%"#{value}"%"
3、配置解析
1、核心配置文件
configuration(配置)
properties(屬性)
settings(設(shè)置)
typeAliases(類型別名)
typeHandlers(類型處理器)
objectFactory(對象工廠)
plugins(插件)
environments(環(huán)境配置)
environment(環(huán)境變量)
transactionManager(事務(wù)管理器)
dataSource(數(shù)據(jù)源)
databaseIdProvider(數(shù)據(jù)庫廠商標(biāo)識(shí))
mappers(映射器)
1.1、環(huán)境配置(environments)
MyBatis 可以配置成適應(yīng)多種環(huán)境,但每個(gè) SqlSessionFactory 實(shí)例只能選擇一種環(huán)境。
Mybatis默認(rèn)的事物管理器是JDBC,連接池:POOLED;
1.2、屬性(properties)
可以通過properties屬性來實(shí)現(xiàn)引用配置文件【db.properties】
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="handhand"/>
</properties>
1.3、類型別名(typeAliases)
類型別名可為 Java 類型設(shè)置一個(gè)縮寫名字。 它僅用于 XML 配置,意在降低冗余的全限定類名書寫
<typeAliases>
<typeAlias type="com.darker.pojo.User" alias="User"/>
</typeAliases>
- 掃描實(shí)體類的包,他的默認(rèn)別名就為這個(gè)類名的首字母小寫
<typeAliases>
<package name="com.darker.pojo">
</typeAliases>
@Alias("helloUser")
1.4、設(shè)置(settings)
設(shè)置名 |
描述 |
有效值 |
默認(rèn)值 |
cacheEnabled |
全局性地開啟或關(guān)閉所有映射器配置文件中已配置的任何緩存。 |
true | false |
true |
lazyLoadingEnabled |
延遲加載的全局開關(guān)。當(dāng)開啟時(shí),所有關(guān)聯(lián)對象都會(huì)延遲加載。 特定關(guān)聯(lián)關(guān)系中可通過設(shè)置 fetchType 屬性來覆蓋該項(xiàng)的開關(guān)狀態(tài)。 |
true | false |
false |
mapUnderscoreToCamelCase |
是否開啟駝峰命名自動(dòng)映射,即從經(jīng)典數(shù)據(jù)庫列名 A_COLUMN 映射到經(jīng)典 Java 屬性名 aColumn。 |
true | false |
False |
logImpl |
指定 MyBatis 所用日志的具體實(shí)現(xiàn),未指定時(shí)將自動(dòng)查找。 |
SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING |
未設(shè)置 |
1.5、映射器(mappers)
注冊綁定我們的mapper文件
<!--每一個(gè)Mapper.xml都需要在Mybatis的核心配置文件中注冊-->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
1.6、生命周期和作用域
SqlSessionFactoryBuilder
- 一旦創(chuàng)建了 SqlSessionFactory,就不再需要它了。
- 局部變量
SqlSessionFactory
- SqlSessionFactory 一旦被創(chuàng)建就應(yīng)該在應(yīng)用的運(yùn)行期間一直存在,沒有任何理由丟棄它或重新創(chuàng)建另一個(gè)實(shí)例。
- SqlSessionFactory 的最佳作用域是應(yīng)用作用域
- 最簡單的就是使用單例模式或者靜態(tài)單例模式。
SqlSession
- SqlSession 的實(shí)例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。
- 用完后關(guān)閉
4、解決屬性名和字段名不一致的問題
解決方法
<select id="getUserById" resultType="com.darker.pojo.User" parameterType="int">
select id,name,password as pwd
from user
where id = #{id}
</select>
結(jié)果集映射
<resultMap id="UserMap" type="com.darker.pojo.User">
<!--column數(shù)據(jù)庫中的字段,property實(shí)體類中的屬性-->
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="password" property="pwd"/>
</resultMap>
<select id="getUserById" resultMap="UserMap" parameterType="int">
select *
from user
where id = #{id}
</select>
5、日志
5.1日志工廠
Mybatis提供的日志工廠

- SLF4J
- LOG4J
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING
- NO_LOGGING
STDOUT_LOGGING標(biāo)準(zhǔn)日志輸出
Opening JDBC Connection
Created connection 1073763441.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@40005471]
==> Preparing: select * from user where id = ?
==> Parameters: 4(Integer)
<== Columns: id, name, password
<== Row: 4, q3142q測試, 23131
<== Total: 1
User{id=4, name='q3142q測試', pwd='23131'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@40005471]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@40005471]
Returned connection 1073763441 to pool.
5.2、Log4j
log4j.rootLogger=DEBUG,console,file
#控制臺(tái)輸出相關(guān)配置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold = DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = [%c] -%m%n
#文件輸出相關(guān)的配置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = ./log/darker.log
log4j.appender.file.MaxFileSize = 10MB
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = [%p][%d{yyyy-MM-dd HH:mm:ss}][%c]%m%n
#日志輸出級別
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
簡單使用
1.導(dǎo)包
import org.apache.log4j.Logger;
2.日志對象,參數(shù)為當(dāng)前類的class
Logger logger = Logger.getLogger(UserDaoTest.class);
3.使用
logger.info("info:進(jìn)入testLog4j");
logger.debug("debug:進(jìn)入testLog4j");
logger.error("error:進(jìn)入testLog4j");
6、分頁
減少數(shù)據(jù)的處理量
使用Limit分頁
select * from user limit startIndex,pageSize
使用RowBounds
SqlSession sqlSession = MybatisUtils.getSqlSession();
//offset,limit.從查詢的第2行開始數(shù),往后面四行數(shù)據(jù)
RowBounds rowBounds = new RowBounds(1, 4);
List<User> userLise = sqlSession.selectList("com.darker.dao.UserMapper.getUserByRowBounds", null, rowBounds);
for (User user : userLise) {
System.out.println(user.toString());
}
sqlSession.close();
7.注解開發(fā)
在接口上實(shí)現(xiàn)
@Select("select * from user")
List<User> getUserListAnn();
配置文件中綁定接口
<mapper class="com.darker.dao.UserMapper"/>
方法中如果有多個(gè)參數(shù),一定要使用
@Param("id")
- 基本類型的參數(shù)或者String類型,需要加上
- 引用類型不需要加上
- 如果只有一個(gè)基本來興,可以忽略
8、多對一的處理
需求:查詢學(xué)生對應(yīng)的老師信息
private long id;
private String name;
//關(guān)聯(lián)老師
private Teacher teacher;
private long id;
private String name;
按照查詢嵌套處理
<select id="getStdentAllInfo" resultMap="StudentTeacher">
select s.id, s.name, s.tid
from student s
</select>
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!-- 復(fù)雜的屬性,需要單獨(dú)處理
association:對象
collection:集合
-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select *
from teacher where id = #{id}
</select>
按照結(jié)果嵌套處理
<select id="getStdentAllInfo2" resultMap="StudentTeacher2">
select s.id sid, s.name sname, t.name tname
from student s
left join teacher t on s.tid = t.id;
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
9、一對多的處理
需求:查詢老師下的學(xué)生信息
private long id;
private String name;
private List<Student> students;
private long id;
private String name;
private int tid;
按照查詢嵌套處理
<select id="getTeacherAllInfo2" resultMap="TeacherSdudent2">
select t.id tid, t.name tname
from teacher t
</select>
<resultMap id="TeacherSdudent2" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" column="tid" javaType="ArrayList" ofType="Student" select="getStudent"/>
</resultMap>
<select id="getStudent" resultType="Student">
select * from student where tid = #{tid}
</select>
按照結(jié)果嵌套處理
<!--案結(jié)果嵌套查詢-->
<select id="getTeacherAllInfo" resultMap="TeacherSdudent">
select t.id tid, t.name tname, s.id sid, s.name sname
from student s,
teacher t
where s.tid = t.id
</select>
<resultMap id="TeacherSdudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!-- javaType:指定屬性的類型
集合中的泛型信息,用ofType獲取-->
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
小結(jié)
1.關(guān)聯(lián) association【多對一】
2.集合 collection 【一對多】
3.javaType 和 ofType
?javaType 用來指定實(shí)體類中的屬性類型
ofType用來指定映射到List或者集合中的POJO類型,泛型中的約束類型
10、動(dòng)態(tài)SQL
在 MyBatis 之前的版本中,需要花時(shí)間了解大量的元素。借助功能強(qiáng)大的基于 OGNL 的表達(dá)式,MyBatis 3 替換了之前的大部分元素,大大精簡了元素種類,現(xiàn)在要學(xué)習(xí)的元素種類比原來的一半還要少。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
if
if標(biāo)簽中tes屬性必輸
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from blog where 1=1
<if test="title != null">
and title like #{title}
</if>
<if test="author != null">
and author like #{author}
</if>
</select>
choose (when, otherwise)
有時(shí)候,我們不想使用所有的條件,而只是想從多個(gè)條件中選擇一個(gè)使用。針對這種情況,MyBatis 提供了 choose 元素,它有點(diǎn)像 Java 中的 switch 語句。
<select id="queryBlogChoose" parameterType="map" resultType="Blog">
select * from blog
<where>
<choose>
<when test="title != null">
and title like #{title}
</when>
<when test="author != null">
and author like #{author}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
trim (where, set)
where 元素只會(huì)在子元素返回任何內(nèi)容的情況下才插入 “WHERE” 子句。而且,若子句的開頭為 “AND” 或 “OR”,where 元素也會(huì)將它們?nèi)コ?/p>
用于動(dòng)態(tài)更新語句的類似解決方案叫做 set。set 元素可以用于動(dòng)態(tài)包含需要更新的列,忽略其它不更新的列。
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title !=null">
title = #{title},
</if>
<if test="author !=null">
author = #{author},
</if>
</set>
where id = #{id}
</update>
foreach
動(dòng)態(tài) SQL 的另一個(gè)常見使用場景是對集合進(jìn)行遍歷(尤其是在構(gòu)建 IN 條件語句的時(shí)候)。
foreach 元素的功能非常強(qiáng)大,它允許你指定一個(gè)集合,聲明可以在元素體內(nèi)使用的集合項(xiàng)(item)和索引(index)變量。它也允許你指定開頭與結(jié)尾的字符串以及集合項(xiàng)迭代之間的分隔符。這個(gè)元素也不會(huì)錯(cuò)誤地添加多余的分隔符,看它多智能!
提示 你可以將任何可迭代對象(如 List、Set 等)、Map 對象或者數(shù)組對象作為集合參數(shù)傳遞給 foreach。當(dāng)使用可迭代對象或者數(shù)組時(shí),index 是當(dāng)前迭代的序號,item 的值是本次迭代獲取到的元素。當(dāng)使用 Map 對象(或者 Map.Entry 對象的集合)時(shí),index 是鍵,item 是值。

<select id="queryBlogForeach" parameterType="map" resultType="Blog">
select * from blog
<where>
<foreach item="id" collection="ids" open="and (" separator="or" close=")">
id = #{id}
</foreach>
</where>
</select>
sql拼接結(jié)果:select * from blog WHERE ( id = ? or id = ? )
SQL片段
將公共的部分抽取出來,方便復(fù)用
使用sql標(biāo)簽抽取公共部分
在要用的時(shí)候是用include標(biāo)簽引用
<sql id="if-title-authoe">
<if test="title != null">
and title like #{title}
</if>
<if test="author != null">
and author like #{author}
</if>
</sql>
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from blog where 1=1
<include refid="if-title-authoe"/>
</select>
11、緩存
Mybatis緩存
- mybatis默認(rèn)定義了兩級緩存:一級緩存和二級緩存
- 默認(rèn)情況下,只有一級緩存開啟,(Sqlsession級別的緩存,也成為本地緩存)
- 二級緩存需要手動(dòng)開啟,他是namespace級別的環(huán)境
- Mybatis還定義了緩存接口Cache,可以通過實(shí)現(xiàn)Cache接口來定義二級緩存
一級緩存
- 映射語句文件中的所有 select 語句的結(jié)果將會(huì)被緩存。
- 映射語句文件中的所有 insert、update 和 delete 語句會(huì)刷新緩存。
- 緩存會(huì)使用最近最少使用算法(LRU, Least Recently Used)算法來清除不需要的緩存。
- 緩存不會(huì)定時(shí)進(jìn)行刷新(也就是說,沒有刷新間隔)。
- 緩存會(huì)保存列表或?qū)ο螅o論查詢方法返回哪種)的 1024 個(gè)引用。
- 緩存會(huì)被視為讀/寫緩存,這意味著獲取到的對象并不是共享的,可以安全地被調(diào)用者修改,而不干擾其他調(diào)用者或線程所做的潛在修改。
- 手動(dòng)清除緩存
sqlSession.clearCache();
二級緩存
要啟用全局的二級緩存,只需要在你的 SQL 映射文件中添加一行
<cache/>
這些屬性可以通過 cache 元素的屬性來修改。比如:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
這個(gè)更高級的配置創(chuàng)建了一個(gè) FIFO 緩存,每隔 60 秒刷新,最多可以存儲(chǔ)結(jié)果對象或列表的 512 個(gè)引用,而且返回的對象被認(rèn)為是只讀的,因此對它們進(jìn)行修改可能會(huì)在不同線程中的調(diào)用者產(chǎn)生沖突。
可用的清除策略有:
LRU – 最近最少使用:移除最長時(shí)間不被使用的對象。
FIFO – 先進(jìn)先出:按對象進(jìn)入緩存的順序來移除它們。
SOFT – 軟引用:基于垃圾回收器狀態(tài)和軟引用規(guī)則移除對象。
WEAK – 弱引用:更積極地基于垃圾收集器狀態(tài)和弱引用規(guī)則移除對象。
默認(rèn)的清除策略是 LRU。
flushInterval(刷新間隔)屬性可以被設(shè)置為任意的正整數(shù),設(shè)置的值應(yīng)該是一個(gè)以毫秒為單位的合理時(shí)間量。 默認(rèn)情況是不設(shè)置,也就是沒有刷新間隔,緩存僅僅會(huì)在調(diào)用語句時(shí)刷新。
size(引用數(shù)目)屬性可以被設(shè)置為任意正整數(shù),要注意欲緩存對象的大小和運(yùn)行環(huán)境中可用的內(nèi)存資源。默認(rèn)值是 1024。
readOnly(只讀)屬性可以被設(shè)置為 true 或 false。只讀的緩存會(huì)給所有調(diào)用者返回緩存對象的相同實(shí)例。 因此這些對象不能被修改。這就提供了可觀的性能提升。而可讀寫的緩存會(huì)(通過序列化)返回緩存對象的拷貝。 速度上會(huì)慢一些,但是更安全,因此默認(rèn)值是 false。
提示 二級緩存是事務(wù)性的。這意味著,當(dāng) SqlSession 完成并提交時(shí),或是完成并回滾,(也就是一級緩存SqlSession .close()之后)但沒有執(zhí)行 flushCache=true 的 insert/delete/update 語句時(shí),緩存會(huì)獲得更新。
原理
用戶查詢的時(shí)候,首先查看二級緩存中有沒有,
如果沒有則去查看一級緩存有沒有,
沒有就會(huì)去查詢數(shù)據(jù)庫。
自定義緩存ehcache
ehcache是一個(gè)分布式緩存
依賴:
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
|