【應用篇】MyBatis學習筆記

MyBatis學習筆記


一 環境配置

1 什麼是MyBatis?

​ MyBatis 是支持普通 SQL 查詢,存儲過程和高級映射的優秀持久層框架。MyBatis 消除了幾乎所有的 JDBC 代碼和參數的手工設置以及結果集的檢索。MyBatis 使用簡單的 XML 或註解用於配置和原始映射,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java 對象)映射成數據庫中的記錄。

​ 特點:使用MyBatis框架時,不需要編寫實現類(原生的JDBC代碼),只需要專注需要執行的sql命令。

2 相關jar包

MyBatis依賴包

3 MyBatis全局配置文件 MyBatis.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!-- dtd文檔,包含命名空間-->
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--全局配置標籤-->
<configuration>
    <!-- 設置LOG4J日誌包生效-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    <typeAliases>
        <!--爲整個包起別名,所有包下的類名爲resultType中屬性值-->
        <package name="cn.thetian.pojo"/>
    </typeAliases>
    <!--環境標籤-->
    <environments default="default">
        <!--聲明可以使用的環境mysql,oracle等-->
        <environment id="default">
            <!--使用原生JDBC事務-->
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/ajax?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--映射標籤,掃描mapper下的xml文件和mapper接口-->
    <mappers>
        <package name="cn.thetian.mapper"/>
    </mappers>
</configuration>

4 LOG4J日誌配置文件

文件名:log4j.properties(必須用這個名字且需要處在src根目錄下,用來調試程序以及記錄日誌)

log4j.rootCategory=INFO, CONSOLE, LOGFILE
#將mapper包設爲DEBUG級別來查看sql語句
log4j.logger.cn.thetian.mapper=DEBUG

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%C %d{YYYY.MM.dd hh:mm:ss} %m %n

log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.file=D://my.log
#true 表示追加 false表示不追加
log4j.appender.LOGFILE.Append = false
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.Conversionattern=%m %n

二 一個簡單的用例

1 設計數據庫

就以一個簡單的用戶表爲例子

在這裏插入圖片描述

2 編寫pojo(例:User)

public class User {
    private int id;
    private String name;
    private String sex;
    private int age;
    private String hobby;
    
    //GetterAndSetter();
    //toString();
}

3 編寫pojoMapper.xml(例:userMapper.xml)

<?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">
<!-- namespace 爲mapper起名,通過namespace.id調用不同的標籤-->
<mapper namespace="cn.thetian.mapper.UserMapper">
    <!-- 
	id:標籤id,一般以方法名命名 
	resultType:返回值類型,未起別名時要用包名加類名錶示返回類型,自動使用AutoMapping特性,即一條記錄對應一個對象,數據庫中字段名要與類的屬性名要保持一致。
	-->
    <select id="selAll" resultType="User">
        select * from user
    </select>
</mapper>

4 測試

/**
 * 測試session中的selectList
 * 1 獲取mybatis.xml全局配置文件的字節流
 * 2 利用工廠模式和構建者模式獲取工廠
 * 3 生產SqlSession
 * 4 利用SqlSession運行指定mapper的xml文件中的namespace+方法名
 * 5 返回List
 */
public class Test01 {
    public static void main(String[] args) throws IOException {
        //獲取MyBatis.xml文件的字節流
        InputStream is = Resources.getResourceAsStream("mybatis.xml");
        //工廠設計模式
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        //生產SqlSession
        SqlSession session = factory.openSession();

        List<User> userList = session.selectList("cn.thetian.mapper.UserMapper.selAll");
        for (User user:userList){
            System.out.println(user.toString());
        }
        //關閉session
        session.close();
    }
}

result:

==> Preparing: select * from user
> Parameters:
<
Total: 5
User{id=1, name=‘張三’, sex=‘男’, age=18, hobby=‘打遊戲,看動漫’}
User{id=2, name=‘李四’, sex=‘男’, age=20, hobby=‘打張三,寫代碼’}
User{id=29, name=‘六六’, sex=‘女’, age=22, hobby=‘主播’}
User{id=30, name=‘張飛’, sex=‘男’, age=8000, hobby=‘俺也一樣’}
User{id=31, name=‘王五’, sex=‘男’, age=666, hobby=‘helloworld’}

三 增刪查改

1 增(insert標籤)

<insert id="insUser" parameterType="User">
    <!-- #{key} 可以獲取到參數值(value)(底層是將user轉爲map)-->
    insert into user values (default, #{name}, #{sex}, #{age}, #{hobby});
</insert>

demo:

try {
	User user = new User();
	user.setName("六六");
	user.setSex("女");
	user.setAge(22);
	user.setHobby("主播");
    
    //將一個user對象作爲參數傳遞到userMapper.xml文件中
	int result = session.insert("cn.thetian.mapper.UserMapper.insUser", user);
	System.out.println(result>0?"添加成功":"添加失敗");
	}catch (Exception e){
    	//出現異常回滾事務
		session.rollback();
	}
//增刪改查需要手動提交事務(可以再MyBatis.xml文件中設置自動提交	)
session.commit();
session.close();

2 刪(delete標籤)

<delete id="delUser" parameterType="user">
	delete from user where id=#{id}
</delete>

demo:

try {
	User user = new User();
	user.setId(31);
	int delete = session.delete("cn.thetian.mapper.UserMapper.delUser", user);
	System.out.println(delete>0?"刪除成功":"刪除失敗");
}catch (Exception e){
    //如果出現異常,回滾事務
	session.rollback();
}
session.commit();
session.close();

3 查

3.1 selectOne

**特點:**查詢一個對象或變量,返回值爲一個基本數據類型或者一個引用數據類型對象

<!-- 查詢user表的行數-->
<select id="selCount" resultType="int">
    select count(*) from user
</select>

demo:

int num = session.selectOne("cn.thetian.mapper.UserMapper.selCount");
System.out.println(num);
session.close();

result:

==> Preparing: select count(*) from user
> Parameters:
<
Total: 1
5

3.2 selectList

特點: 查詢多條記錄,返回集合,舉例 標題二

3.3 selectMap

特點: 將數據庫中某一字段當作map的key(取pojo中的字段) 返回->以參數爲key,以返回結果爲值的map

<select id="selMap" resultType="User">
	select * from user
</select>
Map<Object, Object> map = session.selectMap("cn.thetian.mapper.UserMapper.selMap","name");
System.out.println(map);
session.close();

result:

==> Preparing: select * from user
> Parameters:
<
Total: 5
{李四=User{id=2, name=‘李四’, sex=‘男’, age=20, hobby=‘打張三,寫代碼’}, …

4 改(update)

<update id="updAge">
	update user set age=age+1
</update>

demo:

try {
	int index = session.update("cn.thetian.mapper.UserMapper.updAge");
	System.out.println(index);
}catch (Exception e){
	session.rollback();
}
session.commit();
session.close();

result:

==> Preparing: update user set age=age+1
> Parameters:
<
Updates: 5
5

四 MyBatis接口綁定和註解

1 接口綁定方案

MyBatis接口綁定:創建一個接口來實現maaper.xml文件中的增刪改查,外部通過調用接口來處理sql

UserMapper:

public interface UserMapper {
    List<User> selAll();
    List<User> selById(int id);
    List<User> selMap();
    int selCount();
    List<User> selByLimit(Map map);
    int insUser(User user);
    int delUser(User user);
    int updAge();
}

如果使用註解,可以不用編寫UserMapper.xml配置文件

public interface UserMapper {
    @Select("select * from user")
    List<User> selAll();
    
    @Select("select * from user where id=#{param1}")
    List<User> selById(int id);
    
    @Select("select * from user")
    List<User> selMap();
    
    @Select("select count(*) from user")
    int selCount();
    
    @Select("select * from user limit #{pageNum},#{pageSize}")
    List<User> selByLimit(Map map);
    
    @Insert("insert into user values (default, #{name}, #{sex}, #{age}, #{hobby})")
    int insUser(User user);
    
    @Delete("delete from user where id=#{id}")
    int delUser(User user);
    
    @Update("update user set age=age+1")
    int updAge();
}

當使用接口綁定後可以通過一下方式調用:

InputStream is = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);

在這裏插入圖片描述

效果等同於使用session調用不同的 namespace+id

2 多參數傳遞

使用場景:

當需要一次傳遞多個參數(不使用map)時

使用方法:

在接口中利用註解 @Param 底層原理是將 參數轉化爲map

@Select("select * from account where account=#{param1} and name=#{param2}")
Account selAccNoName(@Param("account") String account, @Param ("name") String name);

demo:

Account account = accountMapper.selAccNoName("1", "劉備");
System.out.println(account);

result:

==> Preparing: select * from account where account=? and name=?
> Parameters: 1(String), 劉備(String)
<
Total: 1
Account{id=1, account=‘1’, password=1, balance=4674.0, name=‘劉備’}

五 動態SQL

由於利用註解實現動態sql很繁瑣,所以牽扯到動態sql的部分任然使用配置文件

1 where用法

需求: 查詢賬戶,查詢條件:無任何條件,按賬戶名查找,按名字查找(可是實現任意單個條件查找或者組合條件查找)

xml:

<select id="selWhereAccNoName" resultType="Account">
	<!--基本數據類型和String 應該用0,1,param1,param2等-->
	select * from account
    <!-- where標籤會自動去掉第一個and-->
	<where>
        <!-- 如果傳遞的賬戶對象中含有有效 賬戶名-->
		<if test="account!=null and account!=''">
			and account=#{account}
		</if>
        <!-- 如果傳遞的賬戶對象中含有有效 名字-->
		<if test="name!=null and name!=''">
			and name=#{name}
		</if>
	</where>
</select>

demo:

Scanner scanner = new Scanner(System.in);
System.out.println("請輸入賬戶名");
String account = scanner.nextLine();
System.out.println("請輸入名字");
String name = scanner.nextLine();//直接回車跳過 相當於空串而不是null
List<Account> accountList = accountMapper.selChooseAccNoName(account, name);
System.out.println(accountList);
session.close();

result:

1 查找所有賬戶,即不輸入賬戶名和名字

請輸入賬戶名

請輸入名字

==> Preparing: select * from account
> Parameters:
<
Total: 3
[Account{id=1, account=‘1’, password=1, balance=4674.0, name=‘劉備’},…

2 按賬戶名查找賬戶

請輸入賬戶名
1
請輸入名字

==> Preparing: select * from account WHERE account=?
> Parameters: 1(String)
<
Total: 1
[Account{id=1, account=‘1’, password=1, balance=4674.0, name=‘劉備’}]

3 按名字查找用戶

請輸入賬戶名

請輸入名字
張飛
==> Preparing: select * from account WHERE name=?
> Parameters: 張飛(String)
<
Total: 1
[Account{id=3, account=‘3’, password=1, balance=5100.0, name=‘張飛’}]

4 按賬戶名和名字查找用戶

請輸入賬戶名
1
請輸入名字
劉備
==> Preparing: select * from account WHERE account=?
> Parameters: 1(String)
<
Total: 1
[Account{id=1, account=‘1’, password=1, balance=4674.0, name=‘劉備’}]

2 choose when otherwise用法

xml:

<select id="selChooseAccNoName" resultType="Account">
    select * from account
	<where>
		<choose>
            <when test="account!=null and account!=''">
	            and account=#{account}
		    </when>
			<when test="name!=null and name!=''">
				and name=#{name}
	        </when>
        </choose>
	</where>
</select>

用法和使用 where+if 語句類似,可實現相同的需求

3 set用法

xml:

<!-- 使用set 去掉生成語句最後面的逗號,在最前方加set-->
<update id="updBalance" parameterType="Account">
	update account
	<set>
        <!-- 爲了避免set語句爲空需要寫一個無關句-->
        id=#{id},
    	<if test="balance!=null">
			balance=#{balance},
    	</if>
	</set>
	where id=#{id}
</update>

demo:

Account newAccount = new Account();
newAccount.setId(1);
newAccount.setBalance(123456);
int i = accountMapper.updBalance(newAccount);
System.out.println(i>0?"修改成功":"修改失敗");
session.close();

result:

==> Preparing: update account SET id=?, balance=? where id=?
> Parameters: 1(Integer), 123456.0(Double), 1(Integer)
<
Updates: 1
修改成功

4 trim用法

使用trim代替set標籤和where子句

xml:

<!-- 使用trim實現where和set
    prefixOverrides 去掉生成語句最前面的"and"
    prefix 在生成語句最前加添加"set(默認後面跟個空格)
    suffix 在生成語句最後放添加"參數"
    suffixOverrides 去掉生成語句最後面的"逗號"
    執行的順序爲>先去掉內容,後添加內容
-->
<update id="pudByTrim" parameterType="Account">
	update account
    <trim prefix="set" suffixOverrides=",">
		<if test="id!=null">
        	id=#{id},
		</if>
		<if test="balance!=null">
	        balance=#{balance},
        </if>
	</trim>
    <trim prefix="where" prefixOverrides="and">
		<if test="id!=null">
	        and id=#{id}
        </if>
	</trim>
</update>

結果與只是用set類似

5 bind用法

需求: 模糊查詢

xml

<select id="selDIY" parameterType="Account" resultType="Account">
	<if test="name!=null and name!=''">
        <!-- 獲取name後再前後各加一個%進行字符匹配-->
        <bind name="name" value="'%'+name+'%'"/>
	</if>
    	select * from account
	<trim prefix="where name like">
	    <if test="name!=null and name!=''">
			#{name}
		</if>
	</trim>
</select>

demo:

Account newAccount = new Account();
newAccount.setName("備");
List<Account> accountList = accountMapper.selDIY(newAccount);
System.out.println(accountList);
session.close();

result:

==> Preparing: select * from account where name like ?
> Parameters: %備%(String)
<
Total: 1
[Account{id=1, account=‘1’, password=1, balance=4674.0, name=‘劉備’}]

6 foreach用法

需求1:in查詢

xml:

<select id="selByIn" parameterType="list" resultType="Account">
	select * from account where id in
    <!-- 使用foreach操作集合-->
	<foreach collection="list" item="var" open="(" close=")" separator=",">
		#{var}
	</foreach>
</select>

demo:

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
List<Account> accountList = accountMapper.selByIn(list);
System.out.println(accountList);
session.close();

result:

==> Preparing: select * from account where id in ( ? , ? , ? )
> Parameters: 1(Integer), 2(Integer), 3(Integer)
<
Total: 3
[Account{id=1, account=‘1’, password=1, balance=4674.0, name=‘劉備’}, …

需求2:批量新增

xml:

<insert id="insBatch" parameterType="list">
	insert into account values
	<trim suffixOverrides=",">
		<foreach collection="list" item="var">
            <!--賬戶名密碼都爲var-->
	        (default ,#{var},#{var},0,'測試名'),
		</foreach>
	</trim>
</insert>

demo:

//需要SqlSession session = factory.openSession(ExecutorType.BATCH);否則會出異常ExecutorException: Executor was closed.
List<Integer> list = new ArrayList<>();
for (int i=10;i<20;i++){
	list.add(i);
}
accountMapper.insBatch(list);
session.commit();
session.close();

result:

==> Preparing: insert into account values (default ,?,?,0,‘測試名’), (default ,?,?,0,‘測試名’), …

==> Parameters: 10(Integer), 10(Integer), 11(Integer), 11(Integer), …

六 關聯查詢

數據庫設計:

在這裏插入圖片描述

POJO設計(假設一個學生只有一個老師,但一個老師可以有多個學生):

在這裏插入圖片描述

需求: 在查詢學生的同時,也要查出他們所關聯的老師

1 業務裝配

顧名思義,就是把兩個相關聯的表單獨查詢出來,在業務層拼裝成聯合查詢的結果

註解:

1:StudentMapper

@Select("select * from student")
List<Student> selAll();

2:TeacherMapper

@Select("select * from teacher where id=#{0}")
Teacher selById(int id);

demo:

public class Test04 {
    public static void main(String[] args) throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = factory.openSession(ExecutorType.BATCH);
        
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        TeacherMapper teacherMapper = session.getMapper(TeacherMapper.class);
        
        List<Student> studentList = studentMapper.selAll();
        for (Student student:studentList){
            student.setTeacher(teacherMapper.selById(student.getTid()));
            System.out.println(student);
        }
        session.close();
    }
}

result:

==> Preparing: select * from student
> Parameters:
<
Total: 6
==> Preparing: select * from teacher where id=?
> Parameters: 1(Integer)
<
Total: 1
Student{id=1, name=‘ZT’, age=15, tid=1, teacher=Teacher{id=1, name=‘孔子’}}
==> Preparing: select * from teacher where id=?
> Parameters: 2(Integer)
<
Total: 1
Student{id=2, name=‘萬志’, age=17, tid=2, teacher=Teacher{id=2, name=‘釋迦摩尼’}}
Student{id=3, name=‘張飛’, age=7999, tid=1, teacher=Teacher{id=1, name=‘孔子’}}
==> Preparing: select * from teacher where id=?
> Parameters: 3(Integer)
<
Total: 1
Student{id=4, name=‘劉備’, age=8001, tid=3, teacher=Teacher{id=3, name=‘王陽明’}}
Student{id=5, name=‘關羽’, age=8000, tid=2, teacher=Teacher{id=2, name=‘釋迦摩尼’}}
Student{id=6, name=‘魯智深’, age=18, tid=3, teacher=Teacher{id=3, name=‘王陽明’}}

2 resultMap標籤

使用resultMap實現關聯單個對象(N+1)

xml:

<resultMap id="teacherMapping" type="student">
	<id column="id" property="id"></id>
	<result column="name" property="name"></result>
	<result column="age" property="age"></result>
	<result column="tid" property="tid"></result>
	<association property="teacher" select="cn.thetian.mapper.TeacherMapper.selById" column="tid"></association>
</resultMap>
<select id="selAll" resultMap="teacherMapping">
	select * from student
</select>

demo:

public class Test04 {
    public static void main(String[] args) throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = factory.openSession(ExecutorType.BATCH);
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<Student> studentList = studentMapper.selAll();
        for (Student student:studentList){
            System.out.println(student);
        }
        session.close();
    }
}

result:

==> Preparing: select * from student
==> Parameters:
====> Preparing: select * from teacher where id=?
> Parameters: 1(Integer)
<
Total: 1
====> Preparing: select * from teacher where id=?
> Parameters: 2(Integer)
<
Total: 1
==> Preparing: select * from teacher where id=?
> Parameters: 3(Integer)
<
Total: 1
<
Total: 6
Student{id=1, name=‘ZT’, age=15, tid=1, teacher=Teacher{id=1, name=‘孔子’}}
Student{id=2, name=‘萬志’, age=17, tid=2, teacher=Teacher{id=2, name=‘釋迦摩尼’}}
Student{id=3, name=‘張飛’, age=7999, tid=1, teacher=Teacher{id=1, name=‘孔子’}}
Student{id=4, name=‘劉備’, age=8001, tid=3, teacher=Teacher{id=3, name=‘王陽明’}}
Student{id=5, name=‘關羽’, age=8000, tid=2, teacher=Teacher{id=2, name=‘釋迦摩尼’}}
Student{id=6, name=‘魯智深’, age=18, tid=3, teacher=Teacher{id=3, name=‘王陽明’}}

3 AutoMapping特性

xml:

<resultMap id="teacherMapping" type="student">
	<id column="id" property="id"></id>
	<result column="name" property="name"></result>
    <result column="age" property="age"></result>
	<result column="tid" property="tid"></result>
    <association property="teacher" javaType="teacher">
		<id column="tid" property="id"></id>
	    <result column="tname" property="name"></result>
    </association>
</resultMap>
<select id="selAll" resultMap="teacherMapping">
	select s.*,t.name tname from student s join teacher t on s.tid=t.id;
</select>

demo:

public class Test04 {
    public static void main(String[] args) throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = factory.openSession(ExecutorType.BATCH);
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<Student> studentList = studentMapper.selAll();
        for (Student student:studentList){
            System.out.println(student);
        }
        session.close();
    }
}

result:

==> Preparing: select s.*,t.name tname from student s join teacher t on s.tid=t.id;
> Parameters:
<
Total: 6
Student{id=1, name=‘ZT’, age=15, tid=1, teacher=Teacher{id=1, name=‘孔子’}}
Student{id=2, name=‘萬志’, age=17, tid=2, teacher=Teacher{id=2, name=‘釋迦摩尼’}}
Student{id=3, name=‘張飛’, age=7999, tid=1, teacher=Teacher{id=1, name=‘孔子’}}
Student{id=4, name=‘劉備’, age=8001, tid=3, teacher=Teacher{id=3, name=‘王陽明’}}
Student{id=5, name=‘關羽’, age=8000, tid=2, teacher=Teacher{id=2, name=‘釋迦摩尼’}}
Student{id=6, name=‘魯智深’, age=18, tid=3, teacher=Teacher{id=3, name=‘王陽明’}}

pper = session.getMapper(StudentMapper.class);
List studentList = studentMapper.selAll();
for (Student student:studentList){
System.out.println(student);
}
session.close();
}
}


**result:**

>==>  Preparing: select s.*,t.name tname from student s join teacher t on s.tid=t.id;  
>==> Parameters:  
><==      Total: 6 
>Student{id=1, name='ZT', age=15, tid=1, teacher=Teacher{id=1, name='孔子'}}
>Student{id=2, name='萬志', age=17, tid=2, teacher=Teacher{id=2, name='釋迦摩尼'}}
>Student{id=3, name='張飛', age=7999, tid=1, teacher=Teacher{id=1, name='孔子'}}
>Student{id=4, name='劉備', age=8001, tid=3, teacher=Teacher{id=3, name='王陽明'}}
>Student{id=5, name='關羽', age=8000, tid=2, teacher=Teacher{id=2, name='釋迦摩尼'}}
>Student{id=6, name='魯智深', age=18, tid=3, teacher=Teacher{id=3, name='王陽明'}}

發佈了16 篇原創文章 · 獲贊 10 · 訪問量 2475
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章