搭建mybatis的環境
匯入相關jar包
- mybatis-3.5.3.jar
- commons-logging-1.1.1.jar
- log4j-1.2.16.jar
- cglib-2.2.2.jar
- asm-3.3.1.jar
- druid-1.1.9.jar
- mysql-connector-java-8.0.16.jar
建立mybatis配置檔案
<?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>
<!--載入類路徑下的屬性檔案-->
<!--這裡我選擇匯入連線池的配置檔案-->
<properties resource="druid.properties" />
<!--設定一個預設連線環境資訊-->
<environments default="mysql_mybatis">
<!--連線環境資訊,取一個任意唯一的名字-->
<environment id="mysql_mybatis">
<!--使用mybatis的事物方式,這裡使用jdbc方式-->
<transactionManager type="jdbc"/>
<!--使用連線池方式連線-->
<dataSource type="pooled">
<!--配置與資料庫互動的四個必要屬性,注意要跟配置檔案裡面定義的相同
在mysql8.0版本以上配置檔案要改成這樣,否則會出現classNotfound異常
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatistest?useSSL=true&serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="293911"/>
-->
<property name="driver" value="${DriverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
</configuration>
資料庫連線池的配置
其中mybatistest是我資料庫的名字,大家可以自行更改。
DriverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatistest?useSSL=true&serverTimezone=GMT
username=root
password=293911
initialSize=5
maxActice=10
maxWait=3000
建立mybatis工具類
像之前的DButils一樣,用於獲取連線,這裡獲取的是mybatis的SqlSession連線,一般寫好後不用怎麼改了,通用的。
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.Reader;
import java.sql.Connection;
public class MybatisUtil {
private static ThreadLocal<SqlSession> threadlocal = new ThreadLocal<SqlSession> ();
private static SqlSessionFactory sqlSessionFactory;
static {
try {
/**
*每 一 個 MyBatis 的 應 用 程 序 都 以 一 個 SqlSessionFactory 對 象 的 實 例 為 核 心 。
SqlSessionFactory 對 象 的 實 例 可 以 通 過 SqlSessionFactoryBuilder 對 象 來 獲 得 。
SqlSessionFactoryBuilder 物件可以從 XML 配置檔案,
或從 Configuration 類的習慣準備的例項中構建 SqlSessionFactory 物件。
*/
//讀取xml檔案
Reader reader = Resources.getResourceAsReader("mybatis.xml");
//構建工廠類
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
catch (Exception e){
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 禁止new出來
*/
private MybatisUtil(){}
/**
* 獲取SQLSession
* @return
*/
public static SqlSession getSqlSession(){
//從當前執行緒獲取SQLSession物件,可以從當前執行緒獲取也可以從工廠類獲取
SqlSession sqlSession = threadlocal.get();
if (sqlSession==null){
sqlSession = sqlSessionFactory.openSession();
//繫結當前執行緒
threadlocal.set(sqlSession);
}
return sqlSession;
}
/**
* 關閉連線物件
*/
public static void closeSqlSession(){
SqlSession sqlSession = threadlocal.get();
if (sqlSession!=null){
sqlSession.close();
//解綁執行緒目的是讓gc快回收。
threadlocal.remove();
}
}
/**
* 測試能否獲取連線,首先確保資料庫已存在!
* @param args
*/
public static void main(String[] args) {
Connection connection = MybatisUtil.getSqlSession().getConnection();
System.out.println(connection!=null?"success":"false");
}
}
測試環境是否搭建成功
1. 新建資料庫mybatistest
2. 建立students表(id,name,sal)
3. 執行mybatis工具類的主函式
資料庫sql語句:
CREATE DATABASE mybatisTest;
CREATE TABLE students( id INT(5) PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(10), sal DOUBLE(8,2) );
執行結果:
建立實體類與表的對映配置檔案
用來寫sql語句並對結果進行封裝。
<?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="np_Student">
<!--
resultMap標籤:對映實體與表
type屬性:表示實體全路徑名
id屬性:為實體與表的對映取一個任意的唯一的名字
-->
<resultMap id="StudentMap" type="model.Student">
<!--
id標籤:對映主鍵屬性
result標籤:對映非主鍵屬性
property屬性:實體的屬性名
column屬性:表的欄位名
一般來說表名跟bean類的屬性名一樣
id代表資料庫表的主鍵
-->
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sal" column="sal"/>
</resultMap>
<!--sql語句寫在這裡
在JDBC中我們通常使用?號作為佔位符,而在Mybatis中,我們是使用#{}作為佔位符
parameterType我們指定了傳入引數的型別
#{}實際上就是呼叫了Student屬性的get方法
注意type或者parameterType裡面放的都是
實體類的全路徑名,即包名.類名
-->
<insert id="add" parameterType="model.Student">
insert into students values(#{id},#{name},#{sal});
</insert>
</mapper>
然後將寫好的mapper配置檔案新增到一開始寫的mybatis配置檔案中去,即在mybatis.xml的/configuration標籤上面去。
<!--載入實體類與表的對映關係配置-->
<mappers>
<mapper resource="StudentMapper.xml" />
</mappers>
測試向資料庫中新增一條資料
1. 建立model包,在model包中新建student類
2. 建立dao包,在dao包中新建add(Student student)方法
Student類程式碼:
package model;
public class Student {
private Integer id;
private String name;
private Double sal;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", sal=" + sal +
'}';
}
public Student() {
}
public Student(Integer id, String name, Double sal) {
this.id = id;
this.name = name;
this.sal = sal;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
}
dao介面類程式碼:
package dao;
import model.Student;
public interface StudentDao {
public void add(Student student);
}
dao實現類程式碼:
package dao;
import model.Student;
import org.apache.ibatis.session.SqlSession;
import utils.MybatisUtil;
public class StudentDaoImpl implements StudentDao {
@Override
public void add(Student student) {
SqlSession sqlSession = null;
try {
//獲取sqlSession物件
sqlSession = MybatisUtil.getSqlSession();
/**
* 呼叫插入方法。
* insert方法的引數有兩個:
* String s = 名稱空間.方法名(id名)
* Object o = 引數列表。
* commit方法引數有true/false,預設為true
* 需要我們手動提交事務。
*/
sqlSession.insert("np_Student.add",student);
sqlSession.commit();
}catch (Exception e){
e.printStackTrace();
//事務回滾
sqlSession.rollback();
throw e;
}finally {
MybatisUtil.closeSqlSession();
}
}
}
專案結構:
Mybatis工作流程
- 通過Reader獲取mybatis對映檔案。
- 通過SqlSessionFactoryBuilder()構建SqlSessionFactory()工廠物件。
- 獲取當前執行緒SQLSession物件。
- 通過SQLSession讀取對映檔案中的操作編號,從而讀取SQL語句。
- 關閉資源。
完成上面步驟後我們就基本上可以用mybatis連線到資料庫並會進行一定的操作了,下面我們將使用mybatis進行資料庫的增刪改查。
使用mybatis對資料庫進行增刪改查
SQLSession物件:
所有的執行語句的方法,提交或回滾事務都由這個例項來進行操作。
SQLSession物件常用方法:
T selectOne(String statement, Object parameter) 只查一個物件,如果為空會拋異常。 List selectList(String statement, Object parameter) 查詢結果以List集合返回 - <K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
查詢結果以Map集合返回 - int insert(String statement, Object parameter) 插入
- int update(String statement, Object parameter) 更新
- int delete(String statement, Object parameter) 刪除
- void commit(boolean force) 提交事務
- void rollback(boolean force) 回滾事務
增加資料改進:
由於我們student表id是自增的,所以我們可以不設定id屬性,使用mybatis的useGeneratedKeys方法來進行改進新增資料。
<!--對於id主鍵自增進行自動生成-->
<insert id="add" parameterType="model.Student" useGeneratedKeys="true" keyProperty="id">
insert into students(name,sal) values(#{name},#{sal});
</insert>
查詢一條記錄:
在StudentMapper.xml中新增select標籤,然後在dao類中新增findStuById(int id)介面,
最後在dao實現類中實現findStuById方法;
新增select標籤:
<!--resultMap就是上面定義的,可以將查詢結果以鍵值對形式返回-->
<select id="findStuById" parameterType="int" resultMap="StudentMap">
select * from students where id = #{id};
</select>
說明一下:MyBatis 中parameter是非常強大的元素,上面的這個示例說明了一個非常簡單的命名引數對映。
引數型別被設定為“int” ,因此這個引數可以被設定成任何內容。原生的型別或簡單資料型別,
比如整型和沒有相關屬性的字串,因此它會完全用引數來替代。對於複雜的引數型別如student類,
它會自動查詢相關屬性,並將它們的值傳遞到預處理語句的引數中去。
dao介面類新增:
public Student findStuById(int id);
dao實現類新增:
@Override
public Student findStuById(int id) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try {
return sqlSession.selectOne("np_Student.findStuById",id);
}catch (Exception e){
e.printStackTrace();
throw e;
}finally {
MybatisUtil.closeSqlSession();
}
}
查詢所有資料:
在StudentMapper.xml中新增select標籤,然後在dao類中新增findAll()介面,最後在dao實現類中通過SQLSession.selectList()方法,實現findAll()方法;
新增select標籤:
<select id="findAll" resultMap="StudentMap">
select * from students;
</select>
dao介面類新增:
public List<Student> findAll();
dao實現類新增:
@Override
public List<Student> findAll() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try{
List<Student> students = sqlSession.selectList("np_Student.findAll");
return students;
}catch (Exception e){
e.printStackTrace();
}finally {
MybatisUtil.closeSqlSession();
}
return null;
}
刪除資料:
在StudentMapper.xml中新增
新增delete標籤:
<delete id="delStudent" parameterType="int">
delete from students where id = #{id};
</delete>
dao介面類新增:
public Student DelStuById(int id);
dao實現類新增:
@Override
public Student DelStuById(int id) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
Student stuById = null;
try {
//查詢所刪除的資料。
stuById = sqlSession.selectOne("np_Student.findStuById",id);
//刪除對應id的資料。
sqlSession.delete("np_Student.delStudent",id);
sqlSession.commit();
}catch (Exception e){
e.printStackTrace();
sqlSession.rollback();
}finally {
MybatisUtil.closeSqlSession();
}
return stuById;
}
更新資料:
在StudentMapper.xml中新增
新增update標籤:
<update id="updateStudent" parameterType="model.Student">
update students set name=#{name},sal=#{sal} where id=#{id};
</update>
dao介面類新增:
public void updateStudent(Student student);
dao實現類新增:
@Override
public void updateStudent(Student student) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try {
sqlSession.update("np_Student.updateStudent",student);
//對於增刪改操作都需要我們手動提交事務,不然操作是不會成功的。
sqlSession.commit();
}catch (Exception e){
e.printStackTrace();
sqlSession.rollback();
}finally {
MybatisUtil.closeSqlSession();
}
}
mybatis分頁:
分頁是指查詢資料庫一定區間範圍內的所有資料,sql語句:select * from students limit 0,5;表示從第0行開始,查詢5條資料。
新增select標籤:
<select id="pagination" parameterType="Map" resultMap="StudentMap">
<!--根據key自動找到對應Map集合的value-->
select * from students limit #{start},#{count};
</select>
dao介面類新增:
public List<Student> pagination(int start, int count);
dao實現類新增:
@Override
public List<Student> pagination(int start, int count) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try {
Map<String,Integer> map = new HashMap<>();
map.put("start",start);
map.put("count",count);
//由於引數只能傳遞一個,所以我們使用map集合,到時候通過key取值。
List<Student> students = sqlSession.selectList("np_Student.pagination", map);
return students;
}catch (Exception e){
e.printStackTrace();
}finally {
MybatisUtil.closeSqlSession();
}
return null;
}
動態sql
MyBatis 的一個強大的特性之一通常是它的動態 SQL 能力。如果你有使用 JDBC 或其他相似框架的經驗,
你就明白條件地串聯 SQL 字串在一起是多麼的痛苦,確保不能忘了空格或在列表的最後省略逗號。
動態 SQL 可以徹底處理這種痛苦。
通常使用動態 SQL 不可能是獨立的一部分,MyBatis 當然使用一種強大的動態 SQL 語言來改進這種情形,
這種語言可以被用在任意對映的 SQL 語句中。 動態 SQL 元素和使用 JSTL 或其他相似的基於 XML 的文字處理器相似。
常用標籤:
- if
- choose(when,otherwise)
- trim(where,set)
- foreach
1.if
在動態 SQL 中所做的最通用的事情是包含部分 where 字句的條件。比如: 多條件查詢。
用法:
<select id="findByCondition" resultMap="StudentMap" parameterType="Map">
select * from students
<where>
<if test="name!=null">
and name=#{name};
</if>
</where>
</select>
看到這裡不知道大家是不是有點疑問呢?我們在JSTL中一般都是用EL表示式如
<c:if test="${name}!=null"}>這樣用的,這裡怎麼直接就寫name呢,不是還沒有賦值嗎?
於是我就想是不是跟map的鍵有關?為了驗證這個想法,我就寫了這麼一個測試類。
測試標籤裡test獲取的值是map集合的鍵:
為了更清楚的看出效果,首先將
<select id="findByCondition" resultMap="StudentMap" parameterType="Map">
select * from students
<where>
<if test="mapname!=null">
and name=#{mapname};
</if>
</where>
</select>
測試方法:
@Test
public void test() {
HashMap<String, Object> map = new HashMap<>();
map.put("name","lisi");
SqlSession sqlSession = MybatisUtil.getSqlSession();
List<Student> students = sqlSession.selectList("np_Student.findByCondition", map);
System.out.println(students);
}
測試結果:
[Student{id=1, name='lisi', sal=11.0}, Student{id=2, name='lee', sal=22.0}, Student{id=3, name='zhangsan', sal=1200.0}]
我資料庫裡面的所有資料都被取了出來,說明了執行findBYCondition方法並沒有拼接name="lisi";
將其更改為map.put("mapname",lisi),注意這裡的mapname和我們的findBYCondition方法裡面定義的
if標籤名字對應,如果這次能查詢出"lisi"這條記錄,說明if標籤裡面定義的正是引數map的鍵的名字。
@Test
public void test() {
HashMap<String, Object> map = new HashMap<>();
map.put("mapname","lisi");
SqlSession sqlSession = MybatisUtil.getSqlSession();
List<Student> students = sqlSession.selectList("np_Student.findByCondition", map);
System.out.println(students);
}
更改後的測試結果:
[Student{id=1, name='lisi', sal=11.0}]
可以證實,
2. choose(when,otherwise)
跟switch用法差不多。
<select id="choose" parameterType="map" resultMap="StudentMap">
select * from students
<where>
<choose>
<when test="name!=null">
and name = #{name}
</when>
<otherwise>
and id = #{id}
</otherwise>
</choose>
</where>
</select>
3. trim(where,set)
修剪元素,很強大,我們之所以可以如此方便的使用動態sql,它功不可沒。
比如:where 元素知道如果由被包含的標記返回任意內容,就僅僅插入"where" 。
而且,如果以"and "或"or"開頭的內容,那麼就會跳過 where 不插入。
如果 where 元素沒有做出你想要的,你可以使用 trim 元素來自定義。
比如,和 where 元素相等的 trim 元素是:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
自己定義一個trim標籤。
<select id="findByCondition" resultMap="StudentMap" parameterType="Map">
select * from students
<!--這裡的trim標籤就相當於where標籤,
如果where後面是以1=1開頭的就將其修剪掉。-->
<trim prefix="where" prefixOverrides="1=1 ">
<if test="name!=null">
1=1 name=#{name};
</if>
</trim>
</select>
於where相同的還有set,同樣可以用trim標籤進行操作。
4. foreach
foreach操作是迭代一個集合, 通常是構建在 in 條件中的。
例子:批量查詢
<!--查詢一個id集合裡面的每個id對應的資料-->
<select id="findStuByIDs" resultMap="StudentMap" parameterType="List">
select * from students where id in
<!--collection代表穿進來的集合名稱,無論陣列還是list集合都是從 "(" 開始 ")" 結束的,分隔符就是","-->
<foreach collection="list" open="(" close=")" separator="," item="item">
#{item}
</foreach>
</select>
進階
1. 多條件模擬查詢:
模糊查詢的sql語句:select* from students where name like %name%;
<!--多條件模擬查詢-->
<select id="findByLike" parameterType="map" resultMap="StudentMap">
select * from students
<where>
<if test="name!=null">
<bind name="newname" value="'%'+name+'%'"/>
and name like #{newname}
</if>
<if test="sal!=null">
<bind name="newsal" value="'%'+sal+'%'"/>
and sal like #{newsal}
</if>
</where>
</select>
2. 動態更新:
set 元素可以被用於動態包含更新的列,而不包含不需更新的
<!--動態更新-->
<!--注意不要漏掉逗號,-->
<update id="updateIfNecessary" parameterType="model.Student">
update students
<set>
<if test="name!=null">
name=#{name},
</if>
<if test="sal!=null">
sal=#{sal},
</if>
</set>
where id = #{id};
</update>
3. 批量刪除:
<!--批量刪除-->
<delete id="delStuInList" parameterType="List">
delete from students where id in
<foreach collection="list" open="(" close=")" separator="," item="item">
#{item}
</foreach>
</delete>
4. 動態插入:
說實話,這裡我有點懵,在網上看別人動態插入的程式碼很長很長的一段,又拼接key,又拼接value,是怕擔心順序亂了?
其實我們只要保證順序一致,就可以不用拼接和判斷是否為空了。
<!--動態插入-->
<insert id="dynamicInsert" parameterType="model.Student" useGeneratedKeys="true" keyProperty="id">
insert into students values
<!--代表從"("開始,")"結束,如果是已","結尾就把它去掉...
其實這段不用這麼寫,我是想熟悉一下這個標籤...-->
<trim prefix="(" suffix=")" suffixOverrides=",">
<!--這裡保證與資料庫裡的表的順序一致,不用判斷是否為空-->
#{id},#{name},#{sal}
</trim>
</insert>
5. 設定別名:
當我們在設定Mapper檔案的時候,需要指定parameterType屬性或者resultType屬性,
該屬性值如果不是一般的型別或String型別,比如是物件型別的話,就需要指定全類名,
如果有多個SQL對映語句的話,那麼每次都指定全類名的話,可能會比較麻煩,是否有更好的方法,
可以簡化一下。你可以通過設定別名的方式來簡化。
<!--設定別名-->
<!--放在mybatis配置檔案中,放置位置在<configuration>標籤下<environments>標籤前-->
<typeAliases>
<!--type是全路徑名,alias是設定的別名-->
<typeAlias type="model.Student" alias="Student"/>
</typeAliases>
之後我們再對parameterType屬性賦值時可以直接用別名就會方便很多。
<!--動態插入-->
<insert id="dynamicInsert" parameterType="Student" useGeneratedKeys="true" keyProperty="id">
insert into students values
<trim prefix="(" suffix=")" suffixOverrides=",">
#{id},#{name},#{sal}
</trim>
</insert>
總結
1. sql寫在xml裡,便於統一和管理,解除了sql與程式程式碼的耦合。
2. Mybatis的事務是預設開啟的,我們需要手動提交事務。
3. 寫Mapper.xml對映檔案時,更像在寫對應的CURD方法,id(方法名),parameterType(傳入引數),resultMap(返回值)。
如果文章有錯的地方歡迎指正,大家互相交流。
最後,碼字不易,喜歡點個贊呀!