MyBatis學習筆記
一 環境配置
1 什麼是MyBatis?
MyBatis 是支持普通 SQL 查詢,存儲過程和高級映射的優秀持久層框架。MyBatis 消除了幾乎所有的 JDBC 代碼和參數的手工設置以及結果集的檢索。MyBatis 使用簡單的 XML 或註解用於配置和原始映射,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java 對象)映射成數據庫中的記錄。
特點:使用MyBatis框架時,不需要編寫實現類(原生的JDBC代碼),只需要專注需要執行的sql命令。
2 相關jar包
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='王陽明'}}