這是我參與8月更文挑戰的第11天,活動詳情查看:8月更文挑戰javascript
來源html
MyBatis 本是 apache 的一個開源項目 iBatisjava
2010 年 由 apache software foundation 遷移到了 google code,而且更名爲 MyBatismysql
2013 年 11 月遷移到 Githubgit
做用github
MyBatis 是一款優秀的持久層框架sql
它支持自定義 SQL、存儲過程以及高級映射數據庫
MyBatis 免除了幾乎全部的 JDBC 代碼以及設置參數和獲取結果集的工做apache
地址緩存
GitHub:github.com/mybatis
Maven:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
複製代碼
數據持久化是指數據由瞬時狀態轉化成持久狀態的過程
持久層是完成持久化工做的代碼
創建測試數據庫
CREATE DATABASE `mybatis`;
CREATE TABLE `user` (
`id` int NOT NULL,
`name` varchar(30) DEFAULT NULL,
`pwd` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
複製代碼
通常步驟
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
public class JDBCDemo {
public static void main(String[] args) throws Exception {
// 註冊驅動
Class.forName("com.mysql.jdbc.Driver");
// 獲取鏈接對象
String url = "jdbc:mysql://localhost:3306/mybaitis";
Connection conn = DriverManager.getConnection(url, "root", "root");
// SQL 語句
String sql = "select * from users where username = ? and password = ? ";
// 獲取執行語句對象
PreparedStatement pst = conn.prepareStatement(sql);
// 設置問號佔位符參數
pst.setObject(1, "小 a");
pst.setObject(2, "123456");
// 調用執行者對象方法,執行SQL語句獲取結果集
ResultSet rs = pst.executeQuery();
// 處理結果集
while (rs.next()) {
System.out.println(rs.getString("username") + " : " + rs.getString("password"));
}
// 關閉資源
rs.close();
pst.close();
conn.close();
}
}
複製代碼
導入 jar 包
maven 構建的項目在 pom.xml 中添加
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
複製代碼
配置文件 mybatis-config-xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/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://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="12345"/>
</dataSource>
</environment>
</environments>
</configuration>
複製代碼
編寫工具類
每一個基於 MyBatis 的應用都是以一個 SqlSessionFactory 的實例爲核心的
SqlSessionFactoryBuilder 經過 XML 配置文件能夠構建出 SqlSessionFactory 實例
SqlSession 包含向數據庫執行 sql 的全部方法,經過 SqlSessionFactory 獲取
utils 包新增工具類
package utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
// 讀取配置文件,獲取 SqlSessionFactory 實例
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/** * 獲取 sqlSession 實例 * * @return SqlSession 實例 */
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
複製代碼
編寫代碼並測試
pojo 包中增長 User 類
package pojo;
public class User {
int id;
String name;
String pwd;
// setter/getter 省略...
}
複製代碼
dao 包中增長 UserDao 接口
package dao;
import pojo.User;
import java.util.List;
public interface UserDao {
List<User> listUsers();
}
複製代碼
dao 包中增長 UserMapper.xml
指定命名空間,select 查詢的 id 對應 UserDao 方法名
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.UserDao">
<select id="listUsers" resultType="pojo.User">
select * from user
</select>
</mapper>
複製代碼
mybatis 配置文件中在 configuration 標籤下添加 mapper 位置信息
<mappers>
<mapper resource="dao/UserMapper.xml"></mapper>
</mappers>
複製代碼
maven pom 文件中要配置開啓包下的 xml 文件資源過濾
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
複製代碼
junit 測試
public class UserDaoTest {
@Test
public void test() {
// 工具類獲取 SqlSession
SqlSession sqlSession = MyBatisUtil.getSqlSession();
// sqlSession 獲取 userDao 實現實例
UserDao userDao = sqlSession.getMapper(UserDao.class);
// 執行 SQL 方法
List<User> users = userDao.listUsers();
System.out.println(users);
}
}
複製代碼
環境配置
MyBatis 能夠配置成適應多種環境,但每一個 SqlSessionFactory 實例只能選擇一種環境
配置多個 environment,使用 default 屬性進行指定
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
複製代碼
properties
屬性能夠在外部進行配置,並能夠進行動態替換,能夠在典型的 Java 屬性文件中配置這些屬性,也能夠在 properties 元素的子元素中設置
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username=root
password=12345
複製代碼
在 mybatis-config 中添加
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="12345" />
</properties>
複製代碼
resource 指定的配置文件中的內容優先級高於標籤內的配置
類型別名
配置 typeAliases
<typeAliases>
<typeAlias type="pojo.User" alias="User"></typeAlias>
<package name="pojo"></package>
</typeAliases>
複製代碼
pojo 上註解
@Alias("User")
public class User {
}
複製代碼
類型別名可爲 Java 類型設置一個縮寫名字。 它僅用於 XML 配置,意在下降冗餘的全限定類名書寫
包別名配置會在包名下面搜索須要的 Java Bean,在沒有註解時,會使用 Bean 的首字母小寫的非限定類名來做爲它的別名
如有註解,則別名爲其註解值
settings
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="logImpl" value="LOG4J"/>
<settings>
複製代碼
cacheEnabled:全局性地開啓或關閉全部映射器配置文件中已配置的任何緩存
lazyLoadingEnabled:延遲加載的全局開關。當開啓時,全部關聯對象都會延遲加載
logImpl:指定 MyBatis 所用日誌的具體實現,未指定時將自動查找
映射器
指定資源路徑
<mappers>
<mapper resource="dao/UserMapper.xml"></mapper>
</mappers>
複製代碼
指定類名,須要 mapper.xml 和 mapper 接口同名且在同一個包下
<mappers>
<mapper class="dao.UserMapper"></mapper>
</mappers>
複製代碼
指定包名,須要 mapper.xml 和 mapper 接口同名且在同一個包下
<mappers>
<package name="dao"></package>
</mappers>
複製代碼
查詢
dao 包中 mapper 接口
public interface UserDao {
User getUserById(int id);
}
複製代碼
UserMapper.xml
<mapper namespace="dao.UserDao">
<select id="getUserById" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
</select>
</mapper>
複製代碼
增刪改
dao 包中 mapper 接口
public interface UserDao {
int addUser(User user);
int updateUser(User user);
int deleteUserById(int id);
}
複製代碼
UserMapper.xml
<insert id="addUser" parameterType="pojo.User">
insert into user values (#{id}, #{name}, #{pwd})
</insert>
<update id="updateUser" parameterType="pojo.User">
update user set name = #{name}, pwd = #{pwd} where id = #{id}
</update>
<delete id="deleteUserById" parameterType="int">
delete from user where id = #{id}
</delete>
複製代碼
增刪改執行方法後,須要提交事務
sqlSession.commit();
// 可使用 sqlSessionFactory.openSession(true) 開啓自動提交
複製代碼
經常使用屬性
resultMap 結果集映射
簡單映射
當查詢出的結果集沒法與 Bean 字段映射時,須要指定 resultMap
例如,數據庫中字段爲 password,Bean 中爲 pwd,能成功映射的字段沒必要要配置
<resultMap type="pojo.User" id="UserMap">
<result column="id" property="id"></result>
<result column="name" property="name"></result>
<result column="password" property="pwd"></result>
</resultMap>
<select id="listUsers" resultMap="UserMap">
select * from user
</select>
複製代碼
複雜映射
例如學生和老師的多對一關係
public class Student {
int id;
String name;
Teacher teacher;
// ...
}
public class Teacher {
int id;
String name;
// ...
}
複製代碼
StudentMapper.xml 中配置 resultMap,association 標籤 select 屬性指向一個查詢,column 做爲查詢參數,javaType
<resultMap id="StudentMap" type="pojo.Student">
<result column="id" property="id"></result>
<result column="name" property="name"></result>
<association property="teacher" column="tid" javaType="pojo.Teacher" select="getTeacher" ></association>
</resultMap>
<select id="listStudents" resultMap="StudentMap">
SELECT * FROM student
</select>
<select id="getTeacher" parameterType="int" resultType="pojo.Teacher">
SELECT * FROM student where id = #{id}
</select>
複製代碼
也能夠用一個查詢完成,須要在 SQL 中對結果集定義別名
<resultMap id="StudentMap" type="pojo.Student">
<result column="sid" property="id"></result>
<result column="sname" property="name"></result>
<association property="teacher" column="tid" javaType="pojo.Teacher">
<result column="tid" property="id"></result>
<result column="tname" property="name"></result>
</association>
</resultMap>
<select id="listStudents" resultMap="StudentMap">
SELECT s.id sid, s.name sname, t.id tid, t.name tname FROM student s, teacher t where s.tid = t.id
</select>
複製代碼
一樣的,老師和學生的一對多關係,可以下解決
// pojo
public class Teacher {
int id;
String name;
List<Student> students;
}
public class Student {
int id;
String name;
int tid;
}
複製代碼
<!-- TeacherMapper.xml -->
<resultMap id="TeacherMap" type="pojo.Teacher">
<result column="tid" property="id"></result>
<result column="tname" property="name"></result>
<collection property="students" ofType="pojo.Student">
<result column="sid" property="id"></result>
<result column="stid" property="tid"></result>
<result column="sname" property="name"></result>
</collection>
</resultMap>
<select id="getTeacher" resultMap="TeacherMap">
select t.id tid, t.name tname, s.id sid, s.name sname, s.tid stid
from teacher t,
student s
where t.id = s.tid
and t.id = #{id}
</select>
複製代碼
association 用來處理多對一,collection 用來處理一對多,javaType 表示屬性的類型,ofType 表示集合中的元素類型
生命週期和做用域使用有誤可能會致使嚴重的併發問題
基本流程
SqlSessionFactoryBuilder -> SqlSessionFactory -> SqlSession -> getMapper()
複製代碼
SqlSessionFactoryBuilder
SqlSessionFactory
SqlSession
日誌工廠
Mybatis 經過使用內置的日誌工廠提供日誌功能。內置日誌工廠將會把日誌工做委託給下面的實現之一:
SLF4J | Apache Commons Logging | Log4j 2 | Log4j | JDK logging
複製代碼
在配置文件的 setting 標籤中能夠設置具體使用哪種日誌實現
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"></setting>
</settings>
複製代碼
STDOUT_LOGGING 是標準日誌實現,能夠直接使用
若使用 LOG4J 須要導入相關包並指定 logImpl 爲 LOG4J
LOG4J
LOG4J 是 Apache 的開源項目,用於實現輸入日誌到控制檯、GUI 組件或文件,可用外部文件靈活配置
pom.xml
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
複製代碼
log4j.properties
#將等級爲 DEBUG 的信息輸出到控制檯和文件
log4j.rootLogger=DEBUG,console,file
#控制檯輸出設置
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=%5p [%d{yyyy-MM-dd HH:mm:ss}][%t][%F:%L] - %m%n
#文件輸出設置
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/log4j.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%5p [%d{yyyy-MM-dd HH:mm:ss}][%t][%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
複製代碼
配置完 pm.xml、mybatis-config.xml、log4j.properties 運行代碼,可見 LOG4J 打印信息在控制檯
也能夠在類中主動使用 Logger 類自定義打印內容
public class LoggerTest {
// 獲取 logger 實例
static Logger logger = Logger.getLogger(LoggerTest.class);
@Test
public void log4jTest() {
// 測試不一樣的日誌級別
logger.info("info => info 內容");
logger.debug("debug => debug 內容");
logger.warn("warn => warn 內容");
logger.error("error => error 內容");
}
}
複製代碼
當數據量過大時,須要分頁以減小數據處理量,並能夠多頁顯示,提高用戶體驗
mysql limit 關鍵字分頁
SELECT * FROM user limit startIndex, pageSize
SELECT * FROM user limit pageSize
// 至關於
SELECT * FROM user limit 0, pageSize
複製代碼
mapper.xml
<select id="listUsersLimit" parameterType="map" resultType="pojo.User">
select * from user limit #{startIndex},#{pageSize}
</select>
複製代碼
dao 或 mapper 接口
public interface UserDao {
List<User> listUsersLimit(Map map);
}
複製代碼
RowBounds 分頁
mapper.xml 中的 SQL 查詢中不作數據量的限制,經過 sqlSession 的查詢方法和 RowBounds 對象進行數量的限制
List<User> users = sqlSession.selectList("dao.UserDao.listUsersByRowBounds",null, new RowBounds(1,2));
複製代碼
CURD 註解
不須要在 mapper.xml 中指定 SQL,直接在註解中傳入 SQL 語句便可,適合簡單的查詢
可使用 @Select/@Update/@Delete/@Insert
public interface UserDao {
@Select("select * from user")
List<User> listUserByAnnotation();
}
複製代碼
參數註解
多個基本類型或 String 類型參數須要經過 @Param 指定名稱,以便在 SQL 語句中使用
不指定也能夠在 SQL 中使用 arg0,arg1 或 param1,param2 等按順序來獲取參數
public interface UserDao {
@Select("select * from user where name = #{name} and pwd = #{pwd}")
User getUserByNameAndPwd(@Param("name") String name, @Param("pwd") String pwd);
}
複製代碼
if
方法傳入 id 爲 0 時不開啓 if 內 and 條件,則查詢所有。傳入 id 不爲 0 時,則查詢指定 id
<select id="listStudentsById" parameterType="int" resultType="pojo.Student">
SELECT *
FROM student
WHERE
<if test="id != 0">
id = #{id}
</if>
</select>
複製代碼
choose,when,otherwise
相似於 switch 條件控制
<select id="listStudentsById" parameterType="int" resultType="pojo.Student">
SELECT *
FROM student
<where>
<choose>
<when test="id == 1">
id = 2
</when>
<when test="id == 2">
and id = 1
</when>
<otherwise>
and id = 3
</otherwise>
</choose>
</where>
</select>
複製代碼
where,trim,set
在上面 if 的例子中,若是 if 條件不知足,不會拼接 if 中的內容,只有一個 where 會致使 SQL 錯誤
能夠經過 where 標籤更優雅的解決,
<select id="listStudentsById" parameterType="int" resultType="pojo.Student">
SELECT *
FROM student
<where>
<if test="id != 0">
id = #{id}
</if>
...
</whrer>
</select>
複製代碼
where 標籤當內部有內容返回時纔會返回 where 條件語句,而且自動去除子句開頭的 and/or
trim 和 where 功能相似,能夠指定拼接子句時要替換的關鍵字,prefix 表示最後返回的子句,prefixOverrides/suffixOverrides 表示須要被刪除或添加的前綴或後綴
<trim prefix="where" prefixOverrides="and |or ">
<choose>
<when test="id == 1">
and id = 2
</when>
<when test="id == 2">
and id = 1
</when>
<otherwise>
and id = 3
</otherwise>
</choose>
</trim>
複製代碼
set 用於實現動態的更新語句
<update id="updateStudent" parameterType="map">
update student
<set>
<if test="id != null">id = #{id},</if>
<if test="name != null">name = #{name},</if>
<if test="tid != null">tid = #{tid}</if>
</set>
</update>
複製代碼
trim 實現 set 功能
<trim prefix="SET" suffixOverrides=",">
...
</trim>
複製代碼
foreach
select * from student where id in (1, 2, 3)
複製代碼
相似於上面的 SQL,當 in 子句的範圍不肯定時,可使用 foreach
<select id="listStudents" parameterType="list" resultType="pojo.Student">
SELECT *
FROM student
<where>
<foreach item="item" index="index" collection="list" open="id in (" separator="," close=")">
#{item}
</foreach>
</where>
</select>
複製代碼
若是 list 元素個數爲 0,則不會返回任何字符串
sql 片斷
sql 標籤訂義的片斷能夠經過 include 引入,提升複用性
<sql id="sql-if-id">
<if test="id != 0">
id = #{id}
</if>
</sql>
複製代碼
在其餘標籤中使用
<select id="listStudentsById" parameterType="int" resultType="pojo.Student">
SELECT *
FROM student
<where>
<include refid="sql-if-id"></include>
</whrer>
</select>
複製代碼
緩存是內存中的臨時數據,能夠提高查詢效率、減小數據庫壓力,適合常常查詢但不常常改變的數據
MyBatis 默認開啓一級緩存,是 SqlSession 級別的緩存
從打開一個 SqlSession 實例開始,重複執行兩次查詢方法,只會執行一次 SQL,返回的數據指向同一對象
如何兩次查詢之間執行了增刪改操做或 sqlSession 調用了 clearCache 方法,則第二次會從新查詢
MyBatis 二級緩存能夠手動開啓,是 namespace 級別的緩存
先要在 myatis-config.xml 中配置開啓緩存
<settings>
<setting name="cacheEnabled" value="true"></setting>
</settings>
複製代碼
在要使用二級緩存的 mapper.xml 經過 cache 標籤中開啓
<cache />
<!--一些屬性-->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
複製代碼
開啓了二級緩存後,在同一個 mapper 中有效
一個 SqlSession 實例關閉後,其中的一級緩存會轉存到二級緩存中,可供另外一個 Sqlsession 實例使用