這是學(xué)習(xí)顏群老師的mybatis教程后整理的筆記
Ⅰ、mybatis介紹與使用
1.mybatis是什么
MyBatis可以簡(jiǎn)化JDBC操作,實(shí)現(xiàn)數(shù)據(jù)的持久化
-
ORM的概念:Object Relational Mapping對(duì)象關(guān)系映射,例如person表對(duì)應(yīng)一個(gè)person對(duì)象
Mybatis是ORM的一個(gè)實(shí)現(xiàn)。有了mybatis,開(kāi)發(fā)人員像操作對(duì)象一樣操作數(shù)據(jù)庫(kù)表
2.怎么用mybatis
-
用maven
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>x.x.x</version> </dependency> 導(dǎo)入jar包,去官網(wǎng)下載(mybatis-x.x.x.jar)
3.第一次用mybatis操作數(shù)據(jù)庫(kù)
構(gòu)建路徑/源路徑:java代碼在的路徑
-
conf.xml:配置數(shù)據(jù)庫(kù)信息和需要加載的映射文件,放在源路徑下
<?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> <!--運(yùn)行的環(huán)境--> <environments default="development"> <!--設(shè)置環(huán)境的id--> <environment id="development"> <transactionManager type="JDBC"/> <!--配置數(shù)據(jù)源類型--> <dataSource type="POOLED"> <!-- 配置數(shù)據(jù)庫(kù)信息 --> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mysqldemo?serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <!-- 加載數(shù)據(jù)庫(kù)映射文件 --> <mapper resource="com/whl/mapper/personMapper.xml"/> </mappers> </configuration>- dataSource標(biāo)簽的type屬性:配置數(shù)據(jù)源類型
- UNPOOLED:傳統(tǒng)的JDBC模式(每次訪問(wèn)數(shù)據(jù)庫(kù)、均需要打開(kāi)、關(guān)閉數(shù)據(jù)庫(kù)連接)
- POOLED:使用數(shù)據(jù)庫(kù)連接池
- JNDI:從tomcat中獲取一個(gè)內(nèi)置的數(shù)據(jù)庫(kù)連接池(數(shù)據(jù)庫(kù)連接池--數(shù)據(jù)源)
- environment標(biāo)簽的id屬性:設(shè)置這個(gè)運(yùn)行環(huán)境的id(測(cè)試、運(yùn)行...)
- environments標(biāo)簽的default的屬性:填寫當(dāng)前運(yùn)行需要的環(huán)境id值,與environment標(biāo)簽的id屬性相對(duì)應(yīng)
- mapper標(biāo)簽的resource屬性:數(shù)據(jù)庫(kù)映射文件mapper.xml的相對(duì)路徑
- property標(biāo)簽:用于配置數(shù)據(jù)庫(kù)連接字符串、用戶名、密碼、驅(qū)動(dòng)等信息
- transactionManager標(biāo)簽:用于設(shè)置事務(wù)方式
- JDBC
- MANAGER
- dataSource標(biāo)簽的type屬性:配置數(shù)據(jù)源類型
-
personMapper.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"> <mapper namespace="com.whl.entity.PersonMapper"> <select id="queryPersonById" resultType="com.whl.entity.Person" parameterType="int"> select * from person where id = #{id} </select> <insert id="addPerson" parameterType="com.whl.entity.Person"> insert into person(id,name,age) values(#{id},#{name},#{age}) </insert> <delete id="deletePersonById" parameterType="int"> delete from person where id = #{id} </delete> <update id="updatePersonById" parameterType="com.whl.entity.Person"> update person set name=#{name},age=#{age} where id=#{id} </update> <select id="queryAllPerson" resultType="com.whl.entity.Person"> select * from person </select> </mapper>mapper標(biāo)簽的namespace屬性:放映射文件的路徑,不用xml后綴
-
SQL語(yǔ)句類型的標(biāo)識(shí)符
- select
- insert
- delete
- update
以上四種標(biāo)簽中的id值來(lái)標(biāo)識(shí)唯一一條SQL語(yǔ)句
-
實(shí)體類Person
package com.whl.entity; public class Person { private int id; private String name; private int age; public Person() { } public Person(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } 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 int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", age=" + age + "]"; } } -
mysql中創(chuàng)建person表如下
-------------------------------------+ | person | CREATE TABLE `person` ( `id` int DEFAULT NULL, `name` varchar(20) DEFAULT NULL, `age` int DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 | -
測(cè)試類
package com.whl.entity; import java.io.IOException; import java.io.Reader; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class TestMybatis { public static void main(String[] args) throws IOException { // Reader reader=Resources.getResourceAsReader("conf.xml"); SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader); //session -- connection SqlSession session = sqlSessionFactory.openSession(); //通過(guò)id值來(lái)定位寫在mapper中的SQL語(yǔ)句 String statement = "com.whl.entity.PersonMapper.queryPersonById"; //查詢一條,用selectOne,第一個(gè)參數(shù)是sql,第二個(gè)參數(shù)是sql中的? Person person = session.selectOne(statement,1); System.out.println(person); //關(guān)閉數(shù)據(jù)庫(kù)連接 session.close(); } }
Ⅱ、基礎(chǔ)方式和mapper動(dòng)態(tài)代理方式
1.基礎(chǔ)方式
第一個(gè)mybatis程序使用的就是基礎(chǔ)方式
<select id="queryPersonById" resultType="com.whl.entity.Person" parameterType="int">
select * from person where id = #{id}
</select>
<select id="queryAllPerson" resultType="com.whl.entity.Person">
select * from person
</select>
- 輸入?yún)?shù)parameterType和輸出參數(shù)resultType,在形式上都只能有一個(gè)
- 輸入?yún)?shù) :是簡(jiǎn)單類型(8個(gè)基本類型+String)是可以使用任何占位符,#{xxxx};如果是對(duì)象類型,則必須是對(duì)象的屬性#{屬性名}
- 輸出參數(shù):如果返回值類型是一個(gè) 對(duì)象(如Student),則無(wú)論返回一個(gè)、還是多個(gè),在resultType都寫成org.lanqiao.entity.Student,即 resultType="org.lanqiao.entity.Student"
- 如果使用的事務(wù)方式為jdbc,則需要手工commit提交,即session.commit();(增刪改需要提交)
- 所有的標(biāo)簽 <select> <update>等,都必須有sql語(yǔ)句,但是sql參數(shù)值可選
2.mapper動(dòng)態(tài)代理方式
約定優(yōu)于配置
與基礎(chǔ)方式的不同點(diǎn):
約定的目標(biāo):省略掉statement,即根據(jù)約定直接可以定位出SQL語(yǔ)句
接口:遵循以下規(guī)定
方法名和mapper.xml文件中標(biāo)簽的id值相同
方法的輸入?yún)?shù)和mapper.xml文件中標(biāo)簽的parameterType類型一致(如果mapper.xml的標(biāo)簽中沒(méi)有 parameterType,則說(shuō)明方法沒(méi)有輸入?yún)?shù))
方法的返回值和mapper.xml文件中標(biāo)簽的 resultType類型一致(無(wú)論查詢結(jié)果是一個(gè) 還是多個(gè)(student、List<Student>),在mapper.xml標(biāo)簽中的resultType中只寫 一個(gè)(Student);如果沒(méi)有resultType,則說(shuō)明方法的返回值為void)
personMapper.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">
<!--
與基礎(chǔ)方式的不同
namespace寫的是PersonMapper接口的全類名
-->
<mapper namespace="com.whl.mapper.PersonMapper">
<select id="queryPersonById" resultType="com.whl.entity.Person" parameterType="int">
select * from person where id = #{id}
</select>
<insert id="addPerson" parameterType="com.whl.entity.Person">
insert into person(id,name,age) values(#{id},#{name},#{age})
</insert>
<delete id="deletePersonById" parameterType="int">
delete from person where id = #{id}
</delete>
<update id="updatePersonById" parameterType="com.whl.entity.Person">
update person set name=#{name},age=#{age} where id=#{id}
</update>
<select id="queryAllPerson" resultType="com.whl.entity.Person">
select * from person
</select>
</mapper>
PersonMapper接口
package com.whl.mapper;
import java.util.List;
import com.whl.entity.Person;
//操作mybatis的接口
public interface PersonMapper {
Person queryPersonById(int id);
List<Person> queryAllPerson();
void addPerson(Person person);
void deletePersonById(int id);
void updatePersonById(Person person);
}
測(cè)試類
package com.whl.entity;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.javassist.expr.NewArray;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.whl.mapper.PersonMapper;
public class Test {
public static void queryPersonById(int id) throws IOException {
Reader reader=Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
//session -- connection
SqlSession session = sqlSessionFactory.openSession();
PersonMapper personMapper=session.getMapper(PersonMapper.class);
Person person=personMapper.queryPersonById(1);
System.out.println(person);
session.close();
}
public static void queryAllPerson() throws IOException {
Reader reader=Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
PersonMapper personMapper=session.getMapper(PersonMapper.class);
List<Person> persons=personMapper.queryAllPerson();
System.out.println(persons);
session.close();
}
public static void addPerson(Person person) throws IOException {
Reader reader=Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
PersonMapper personMapper=session.getMapper(PersonMapper.class);
personMapper.addPerson(person);
session.commit();
System.out.println("增加成功");
session.close();
}
public static void deletePersonById(int id) throws IOException {
Reader reader=Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
PersonMapper personMapper=session.getMapper(PersonMapper.class);
personMapper.deletePersonById(id);
session.commit();
System.out.println("刪除成功");
session.close();
}
public static void updatePersonById(Person person) throws IOException {
Reader reader=Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
PersonMapper personMapper=session.getMapper(PersonMapper.class);
personMapper.updatePersonById(person);
session.commit();
System.out.println("修改成功");
session.close();
}
public static void main(String[] args) throws IOException {
//queryPersonById(1);
//addPerson(new Person(3,"xxx",19));
//updatePersonById(new Person(2,"whl",19));
deletePersonById(3);
queryAllPerson();
}
}
測(cè)試類中最重要的代碼
StudentMapper studentMapper = session.getMapper(StudentMapper.class) ;
studentMapper.方法();
通過(guò)session對(duì)象獲取接口(session.getMapper(接口.class);),再調(diào)用該接口中的方法,程序會(huì)自動(dòng)執(zhí)行該方法對(duì)應(yīng)的SQL。這樣就不需要statement和selectone了
匹配的過(guò)程:
- 根據(jù) 接口名 找到 mapper.xml文件(根據(jù)的是namespace=接口全類名)
- 根據(jù) 接口的方法名 找到 mapper.xml文件中的SQL標(biāo)簽 (方法名=SQL標(biāo)簽Id值)
以上2點(diǎn)可以保證: 當(dāng)我們調(diào)用接口中的方法時(shí),程序能自動(dòng)定位到某一個(gè)Mapper.xml文件中的sqL標(biāo)簽
習(xí)慣:SQL映射文件(mapper.xml)和接口放在同一個(gè)包中(注意修改conf.xml中加載mapper.xml文件的路徑)
3.優(yōu)化
-
可以將配置信息單獨(dú)放入db.properties文件中,然后再動(dòng)態(tài)引入
形式:kv對(duì)
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mysqldemo?serverTimezone=UTC username=root password=123456db.properties放在源路徑
在conf.xml中用properties標(biāo)簽引入
<configuration> <properties resource="db.properties"/> ... </configuration>通過(guò)${key}來(lái)取值(類似于EL表達(dá)式)
<property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> -
MyBatis全局參數(shù),在conf.xml中設(shè)置(一般不進(jìn)行手動(dòng)設(shè)置,使用默認(rèn)值)
在setting標(biāo)簽中進(jìn)行設(shè)置
<settings> <setting name="cacheEnabled" value="false" /> <setting name="lazyLoadingEnabled" value="false" /> </settings> -
別名(單個(gè)別名、批量別名),在conf.xml中的typeAliases標(biāo)簽進(jìn)行設(shè)置
<typeAliases> <!-- 單個(gè)別名 (別名 忽略大小寫) --> <!-- <typeAlias type="org.lanqiao.entity.Student" alias="student"/> --> <!-- 批量定義別名 (別名 忽略大小寫),以下會(huì)自動(dòng)將該包中的所有類 批量定義別名: 別名就是類名(不帶包名,忽略大小寫) --> <package name="org.lanqiao.entity"/> </typeAliases>除了自定義別名外,MyBatis還內(nèi)置了一些常見(jiàn)類的別名。
Ⅲ、類型處理器
類型處理器:把Java代碼中的類型轉(zhuǎn)換成JDBC類型
1.自帶類型處理器
MyBatis自帶一些常見(jiàn)的類型處理器

2.自定義MyBatis類型處理器
例:
實(shí)體類Student : boolean stuSex;
true:男
false:女
表student: number stuSex
1:男
0:女
-
創(chuàng)建轉(zhuǎn)換器
直接實(shí)現(xiàn)TypeHandler接口
-
或繼承BaseTypeHandler
package org.lanqiao.converter; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; //BaseTypeHandler<java類型> public class BooleanAndIntConverter extends BaseTypeHandler<Boolean>{ //java(boolean)-DB(number) /* * ps:PreparedStatement對(duì)象 * i:PreparedStatement對(duì)象操作參數(shù)的位置 * parameter:java值 * jdbcType:jdbc操作的數(shù)據(jù)庫(kù)類型 */ @Override public void setNonNullParameter(PreparedStatement ps, int i, Boolean parameter, JdbcType jdbcType) throws SQLException { if(parameter) { //1 ps.setInt(i, 1); }else { // 0 ps.setInt(i, 0); } } //db(number)->java(boolean) @Override public Boolean getNullableResult(ResultSet rs, String columnName) throws SQLException { int sexNum = rs.getInt(columnName) ;//rs.getInt("stuno") ; return sexNum == 1?true:false ; } @Override public Boolean getNullableResult(ResultSet rs, int columnIndex) throws SQLException { int sexNum = rs.getInt(columnIndex) ;//rs.getInt(1) return sexNum == 1?true:false ; } @Override public Boolean getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { int sexNum = cs.getInt(columnIndex) ;//rs.getInt(1) return sexNum == 1?true:false ; } }
-
配置conf.xml,通過(guò)typeHandler標(biāo)簽配置類型轉(zhuǎn)換器
<typeHandlers> <typeHandler handler="org.lanqiao.converter.BooleanAndIntConverter" javaType="Boolean" jdbcType="INTEGER" /> </typeHandlers> -
配置mapper.xml
<select id="queryStudentByStunoWithConverter" parameterType="int" resultMap="studentResult" ><!--注意這里使用了resultMap而不是resultType--> select * from student where stuno = #{stuno} </select> <!--配置了一些表字段名到屬性名的映射--> <resultMap type="student" id="studentResult"> <!-- 分為主鍵id 和非主鍵 result--> <id property="stuNo" column="stuno" /> <result property="stuName" column="stuname" /> <result property="stuAge" column="stuage" /> <result property="graName" column="graname" /> <result property="stuSex" column="stusex" javaType="boolean" jdbcType="INTEGER"/> </resultMap>何時(shí)使用resultMap?
- 如果類中屬性名和表中的字段名能夠合理識(shí)別(stuNo -stuno)則可以使用resultType;否則(id-stuno)使用resultMap
- 如果類中屬性和表中的字段類型能夠合理識(shí)別(String-varchar2),則可以使用resultType;否則(boolean-number)使用resultMap
Ⅳ、輸入?yún)?shù)
1.簡(jiǎn)單類型
-
{}、${}的區(qū)別
-
{任意值}
${value} ,其中的標(biāo)識(shí)符只能是value
-
{}自動(dòng)給String類型加上' '(自動(dòng)類型轉(zhuǎn)換)
${}原樣輸出,但是適合于動(dòng)態(tài)排序(動(dòng)態(tài)字段)
select stuno,stuname,stuage from student where stuname = #{value} select stuno,stuname,stuage from student where stuname = '${value}' 動(dòng)態(tài)排序: select stuno,stuname,stuage from student order by ${value} asc -
{}可以防止SQL注入,${}不防止
-
相同之處:都可以獲取對(duì)象的值(嵌套類型對(duì)象)
2.模糊查詢獲取對(duì)象值
- 方式一:用#{}
select stuno,stuname,stuage from student where stuage= #{stuAge} or stuname like #{stuName}
Student student = new Student();
student.setStuAge(24);
student.setStuName("%w%");//在set時(shí)就加入了通配符
List<Student> students = studentMapper.queryStudentBystuageOrstuName(student) ;//接口的方法->SQL
-
方式二:用${}
student.setStuName("w");select stuno,stuname,stuage from student where stuage= #{stuAge} or stuname like '%${stuName}%'
3.輸入對(duì)象為HashMap
在框架學(xué)習(xí)中HashMap一般是這樣聲明的(String,Object對(duì))
Map<String,Object> map = new HashMap<>();
map.put("stuAge",19);
<select id="..." parameterType="HashMap" resultType="...">
select ... where stuage= #{stuAge}<!--通過(guò)stuAge取到了map中的值作為參數(shù)-->
</select>
Ⅴ、輸出參數(shù)
輸出參數(shù)的四種情況:
- 簡(jiǎn)單類型(8個(gè)基本+String)
- 輸出參數(shù)為實(shí)體對(duì)象類型
- 輸出參數(shù)為實(shí)體對(duì)象類型的集合 :雖然輸出類型為集合,但是resultType依然寫 集合的元素類型(resyltType="Student")
- 輸出參數(shù)類型為HashMap
--HashMap本身是一個(gè)集合,可以存放多個(gè)元素,
但是根據(jù)提示發(fā)現(xiàn) 返回值為HashMap時(shí) ,查詢的結(jié)果只能是1個(gè)學(xué)生(no,name);
-->結(jié)論:一個(gè)HashMap 對(duì)應(yīng)一個(gè)學(xué)生的多個(gè)元素(多個(gè)屬性) [一個(gè)map,一個(gè)學(xué)生]
1.resultMap
當(dāng)實(shí)體類的屬性、數(shù)據(jù)表的字段:類型、名字不同時(shí),使用resultMap建立起字段名與屬性名的映射
<resultMap type="student" id="queryStudentByIdMap">
<!-- 指定類中的屬性和表中的字段對(duì)應(yīng)關(guān)系-->
<id property="stuNo" column="id" />
<result property="stuName" column="name" />
</resultMap>
- 用id標(biāo)簽指定主鍵的映射
- 用result標(biāo)簽指定非主鍵的映射
- property屬性的值為屬性名,column屬性的值為字段名
2.resultType+HashMap
通過(guò)hashmap把屬性名與字段名進(jìn)行對(duì)應(yīng)
- 可用AS取別名的方式進(jìn)行對(duì)應(yīng),也可以在select語(yǔ)句字段名后空格給出屬性名
<!--注意一定要把返回類型改成hashmap-->
<select id="queryPersonByIdWithHashMap" parameterType="int" resultType="hashmap">
select id as personId,name as personName,age as personAge from person where id=#{id}
</select>
或
<select id="queryPersonByIdWithHashMap" parameterType="int" resultType="hashmap">
select id "personId",name "personName",age "personAge" from person where id=#{id}
</select>
<!--雙引號(hào)可加可不加-->
HashMap<String, Object> person=personMapper.queryPersonByIdWithHashMap(1);
System.out.println(person);
//{personName=lxl, personAge=19, personId=1}
注意:以hashmap作為返回類型,其中hashmap存儲(chǔ)的是某一個(gè)學(xué)生的多個(gè)屬性的鍵值對(duì),而不是多個(gè)學(xué)生
如何返回多個(gè)學(xué)生?List<HashMap<String, Object>> persons
List<HashMap<String, Object>> persons = personMapper.queryPersonAllWithHashMap();
System.out.println(persons);
//[{name=lxl, id=1, age=19}, {name=whl, id=2, age=19}]
Ⅵ、動(dòng)態(tài)SQL
1.動(dòng)態(tài)生成where子句
<select id="queryPersonByNameAndAge" parameterType="person" resultMap="person_map">
select id,name,age from person
<where>
<if test="personName!=null and personName!=''">
and name = #{personName}
</if>
<if test="personAge!=null and personAge!=0">
and age = #{personAge}
</if>
</where>
</select>
<resultMap type="person" id="person_map">
<id property="personId" column="id"/>
<result property="personName" column="name"/>
<result property="personAge" column="age"/>
</resultMap>
- where標(biāo)簽:寫了后就不用在SQL中手寫where關(guān)鍵字了
- if標(biāo)簽
- test屬性寫條件表達(dá)式,為true時(shí)執(zhí)行標(biāo)簽內(nèi)的SQL
- 注意:條件表達(dá)式中寫的是對(duì)屬性名的判斷,不同條件用and、or連接
- 第一個(gè)if標(biāo)簽中的SQL是可以加and關(guān)鍵字的,因?yàn)閙ybatis會(huì)自動(dòng)忽略這個(gè)and關(guān)鍵字
我踩的坑:怎么執(zhí)行查詢都是null,后來(lái)發(fā)現(xiàn)沒(méi)有配置resultMap。只要是字段和屬性名不一樣就必須配置!
2.輸入?yún)?shù)為集合或數(shù)組
輸入?yún)?shù)是list的情況
<!--屬性名和字段的映射-->
<resultMap type="person" id="person_map">
<id property="personId" column="id"/>
<result property="personName" column="name"/>
<result property="personAge" column="age"/>
</resultMap>
<select id="queryPersonByNolist" parameterType="list" resultMap="person_map">
select * from person
<where>
<if test="list!=null and list.size>0">
<foreach collection="list" open=" and id in (" close=")" item="personNo" separator=",">
#{personNo}
</foreach>
</if>
</where>
</select>
- 所有集合在mapper中都用list來(lái)指代,并不使用集合本身的名稱
- foreach標(biāo)簽用于迭代集合
- collection屬性:指定要遍歷的集合
- open和close:只遍歷集合中元素,不遍歷集合前后的SQL語(yǔ)句。用這兩個(gè)屬性指定前后的SQL
- item:給集合中每個(gè)元素取一個(gè)臨時(shí)的名稱。這個(gè)名稱是無(wú)所謂的,只要和標(biāo)簽中#{}的一樣就行了
- separator:分隔符,不加就會(huì)變成"id in (123)",需要的是"id in (1,2,3)"
- 用#{}來(lái)取集合中元素的值
personNos.add(1);
personNos.add(2);
//personNos.add(3);
List<Person> persons=personMapper.queryPersonByNolist(personNos);;
System.out.println(persons);
//[Person [personId=1, personName=lxl, personAge=19], Person [personId=2, personName=whl, personAge=19]]
輸入?yún)?shù)是int數(shù)組的情況:
<select id="queryPersonByNoarray" parameterType="int[]" resultMap="person_map">
select * from person
<where>
<if test="array!=null and array.length>0">
<foreach collection="array" open=" and id in (" close=")" item="personNo" separator=",">
#{personNo}
</foreach>
</if>
</where>
</select>
- 需要注意集合的大小是size,數(shù)組是length
- 傳入的簡(jiǎn)單類型的數(shù)組在mapper中一律以array表示
int[] personNos=new int[] {1,2,3};
List<Person> persons=personMapper.queryPersonByNoarray(personNos);;
System.out.println(persons);
//[Person [personId=1, personName=lxl, personAge=19], Person [personId=2, personName=whl, personAge=19], Person [personId=3, personName=abc, personAge=19]]
輸入?yún)?shù)是對(duì)象數(shù)組的情況:
<select id="queryPersonByObjectarray" parameterType="Object[]" resultMap="person_map">
select * from person
<where>
<if test="array!=null and array.length>0">
<foreach collection="array" open=" and id in (" close=")" item="person" separator=",">
#{person.personId}
</foreach>
</if>
</where>
</select>
- 注意:所有類的對(duì)象數(shù)組的parameterType都是Object[]
- 級(jí)聯(lián)獲取對(duì)象中的屬性值
Person p1=new Person();
Person p2=new Person();
Person p3=new Person();
p1.setPersonId(1);
p2.setPersonId(2);
p3.setPersonId(3);
Person[] persons1=new Person[] {p1,p2,p3};
List<Person> result=personMapper.queryPersonByObjectarray(persons1);;
System.out.println(result);
//[Person [personId=1, personName=lxl, personAge=19], Person [personId=2, personName=whl, personAge=19], Person [personId=3, personName=abc, personAge=19]]
3.提取SQL片段
<sql id="sql1">
select * from person
</sql>
<select id="queryPersonByObjectarray" parameterType="Object[]" resultMap="person_map">
<include refid="sql1"></include>
<where>
<if test="array!=null and array.length>0">
<foreach collection="array" open=" and id in (" close=")" item="person" separator=",">
#{person.personId}
</foreach>
</if>
</where>
</select>
- 用sql標(biāo)簽提取出常用的SQL語(yǔ)句,id標(biāo)識(shí)唯一的SQL語(yǔ)句
- 用include標(biāo)簽中的refid屬性指定要使用的SQL語(yǔ)句的id
Ⅶ、關(guān)聯(lián)查詢


一對(duì)一:association
一對(duì)多:collection
1.一對(duì)一
方法一:
1.建一個(gè)類,繼承自屬性多的類,再把多出來(lái)的屬性寫在這個(gè)新類里
package com.whl.entity;
public class PersonBusiness extends Person{
private int cardId;
private String cardInfo;
public int getCardId() {
return cardId;
}
public void setCardId(int cardId) {
this.cardId = cardId;
}
public String getCardInfo() {
return cardInfo;
}
public void setCardInfo(String cardInfo) {
this.cardInfo = cardInfo;
}
@Override
public String toString() {
return super.toString()+"PersonBusiness [cardId=" + cardId + ", cardInfo=" + cardInfo + "]";
}
}
- 注意tostring方法要重寫并調(diào)用父類的tostring!
2.設(shè)置person表的cardid為外鍵
3.寫SQL
<select id="queryPersonByIdOO" parameterType="int" resultType="PersonBusiness">
select p.*,c.* from person as p inner join personcard as c
on p.cardid=c.cardid
where p.id=#{id}
</select>
- 注意返回類型為PersonBusiness(第三個(gè)類)
4.執(zhí)行
PersonBusiness personBusiness=personMapper.queryPersonByIdOO(1);
System.out.println(personBusiness);
//Person [id=1, name=lxl, age=19]PersonBusiness [cardId=1, cardInfo=lxl info...]
我踩的坑:第一次查詢結(jié)果只顯示了PersonBusiness增加的屬性,person中的顯示都為null或者0,最后發(fā)現(xiàn)是數(shù)據(jù)庫(kù)字段名與屬性名不一致。修改一致后就可正常顯示
方法二:寫一個(gè)person類和personcard類,在person類中有一個(gè)personcard對(duì)象
public class PersonCard {
private int cardId;
private String cardInfo;
}
public class Person {
private int id;
private String name;
private int age;
private PersonCard card;//把PersonCard類的對(duì)象作為Person類的屬性
}
<select id="queryPersonByIdOO" parameterType="int" resultMap="person_card_map">
select p.*,c.* from person as p inner join personcard as c
on p.cardid=c.cardid
where p.id=#{id}
</select>
<resultMap type="person" id="person_card_map">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<association property="card" javaType="PersonCard" >
<id property="cardId" column="cardid"/>
<result property="cardInfo" column="cardinfo"/>
</association>
</resultMap>
- resultMap中的type屬性為person,因?yàn)閜erson包含了PersonCard
- 一對(duì)一關(guān)聯(lián):用association標(biāo)簽指定一對(duì)一
- property屬性的值為Person類中PersonCard對(duì)象成員的名稱
- javaType屬性指定了card對(duì)象的類名
- id和result標(biāo)簽指定了personcard表與personcard對(duì)象字段名、屬性名之間的映射關(guān)系
Person person=personMapper.queryPersonByIdOO(1);
System.out.println(person);
//Person [id=1, name=lxl, age=19, card=PersonCard [cardId=1, cardInfo=lxl info...]]
2.一對(duì)多
1.寫xml
<select id="queryPersonByClassId" parameterType="int" resultMap="person_class_map">
select c.*,p.* from person as p
inner join personclass c
on c.classid=p.classid
where c.classid=#{classid}
</select>
<resultMap type="PersonClass" id="person_class_map">
<id property="classId" column="classid"/>
<result property="className" column="classname"/>
<collection property="persons" ofType="Person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
</collection>
</resultMap>
- collection標(biāo)簽:指定一對(duì)多關(guān)聯(lián)查詢
2.寫屬性類
public class PersonClass {
private int classId;
private String className;
List<Person> persons;
...
}
public class Person {
private int id;
private String name;
private int age;
private PersonCard card;
...
}
- 班級(jí)和學(xué)生為一對(duì)多關(guān)系,所以在PersonClass中用List保存學(xué)生
- card對(duì)象作為Person類的成員
3.測(cè)試運(yùn)行
PersonClass personClass=personMapper.queryPersonByClassId(1);
System.out.println(personClass);
//PersonClass [classId=1, className=c1, persons=[Person [id=1, name=lxl, age=19, card=null]]]
Ⅷ、日志
可以通過(guò)日志信息,相信的閱讀mybatis執(zhí)行情況(觀察mybatis實(shí)際執(zhí)行sql語(yǔ)句以及SQL中的參數(shù)和返回結(jié)果)
以log4j為例
引入log4j.jar
-
在conf.xml中開(kāi)啟日志
<settings> <!-- 開(kāi)啟日志,并指定使用的具體日志 --> <setting name="logImpl" value="LOG4J"/> </settings>如果不指定,Mybatis就會(huì)根據(jù)以下順序 尋找日志
SLF4J →Apache Commons Logging →Log4j 2 → Log4j →JDK logging -
編寫配置日志輸出文件
在src下創(chuàng)建log4j.properties
log4j.rootLogger=DEBUG, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
日志級(jí)別:DEBUG<INFO<WARN<ERROR
如果設(shè)置為info,則只顯示 info及以上級(jí)別的信息;
建議:在開(kāi)發(fā)時(shí)設(shè)置debug,在運(yùn)行時(shí)設(shè)置為info或以上。
Ⅸ、延遲加載
也稱“懶加載”
什么是延遲加載:
在關(guān)聯(lián)查詢(一對(duì)一、一對(duì)多、多對(duì)多)中,如果不采用延遲加載(立即加載),查詢時(shí)會(huì)將一和多都查詢,班級(jí)、班級(jí)中的所有學(xué)生。如果想要暫時(shí)只查詢1的一方,而多的一方先不查詢而是在需要的時(shí)候再去查詢-->延遲加載
-
開(kāi)啟延遲加載conf.xml配置settings
<settings> <!-- 開(kāi)啟延遲加載 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 關(guān)閉立即加載 --> <setting name="aggressiveLazyLoading" value="false"/> </settings> -
寫接口
List<Person> queryPersonByIdOO(); -
寫SQL
<select id="queryPersonByIdOO" resultMap="person_card_map"> select * from person </select> <resultMap type="person" id="person_card_map"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="age" column="age"/> <association property="card" javaType="com.whl.entity.PersonCard" select="com.whl.mapper.PersonCardMapper.queryCardById" column="cardid"> </association> </resultMap>新建PersonCardMapper,注意要在conf.xml加載該映射!
<mapper namespace="com.whl.mapper.PersonCardMapper"> <select id="queryCardById" parameterType="int" resultType="com.whl.entity.PersonCard"> select * from personcard where cardid = #{cardId} </select> </mapper>- association標(biāo)簽表示一對(duì)一,collection表示一對(duì)多
- property表示person類中的card屬性
- javaType寫card的類名
- select屬性寫PersonCardMapper中的sql的id。形式:namespace.id
- column關(guān)聯(lián)的外鍵,作為queryCardById的輸入?yún)?shù)
- queryCardById中的resultType屬性:寫card類的全名
- association標(biāo)簽表示一對(duì)一,collection表示一對(duì)多
-
測(cè)試運(yùn)行
List<Person> persons=personMapper.queryPersonByIdOO(); System.out.println(persons); for(Person person:persons) { //System.out.println(person.getId()+"----"+person.getName()); PersonCard personCard=person.getCard(); System.out.println(person); } //Person [id=1, name=lxl, age=19, card=PersonCard [cardId=1, cardInfo=lxl info...]] //Person [id=2, name=whl, age=19, card=PersonCard [cardId=2, cardInfo=whl info...]]
Ⅹ、查詢緩存
一級(jí)緩存 :同一個(gè)SqlSession對(duì)象
MyBatis默認(rèn)開(kāi)啟一級(jí)緩存,如果用同樣的SqlSession對(duì)象查詢相同的數(shù)據(jù),則只會(huì)在第一次 查詢時(shí) 向數(shù)據(jù)庫(kù)發(fā)送SQL語(yǔ)句,并將查詢的結(jié)果 放入到SQLSESSION中(作為緩存存在);后續(xù)再次查詢?cè)撏瑯拥膶?duì)象時(shí),則直接從緩存中查詢?cè)搶?duì)象即可(即省略了數(shù)據(jù)庫(kù)的訪問(wèn))
二級(jí)緩存:MyBatis默認(rèn)情況沒(méi)有開(kāi)啟二級(jí)緩存,需要手工打開(kāi)。
1.conf.xml
<!-- 開(kāi)啟二級(jí)緩存 -->
<setting name="cacheEnabled" value="true"/>
2.在具體的mapper.xml中聲明開(kāi)啟(studentMapper.xml中)
<mapper namespace="org.lanqiao.mapper.StudentMapper">
<cache/>
<mapper/>
- cache標(biāo)簽:聲明此namespace開(kāi)啟二級(jí)緩存
根據(jù)異常提示:NotSerializableException可知,MyBatis的二級(jí)緩存 是將對(duì)象 放入硬盤文件中
準(zhǔn)備緩存的對(duì)象,必須實(shí)現(xiàn)了序列化接口(如果開(kāi)啟的緩存Namespace="org.lanqiao.mapper.StudentMapper"),可知序列化對(duì)象為Student,因此需要將Student序列化(序列化Student類,以及Student的級(jí)聯(lián)屬性、和父類)
- 觸發(fā)將對(duì)象寫入二級(jí)緩存的時(shí)機(jī):SqlSession對(duì)象的close()方法。
Mybatis自帶二級(jí)緩存:[同一個(gè)namespace]生成的mapper對(duì)象
回顧:namespace的值就是接口的全類名(包名.類名),通過(guò)接口可以產(chǎn)生代理對(duì)象(studentMapper對(duì)象)
namespace決定了studentMapper對(duì)象的產(chǎn)生
結(jié)論:只要產(chǎn)生的xxxMapper對(duì)象 來(lái)自于同一個(gè)namespace,則 這些對(duì)象 共享二級(jí)緩存。
注意:二級(jí)緩存 的范圍是同一個(gè)namespace, 如果有多個(gè)xxMapper.xml的namespace值相同,則通過(guò)這些xxxMapper.xml產(chǎn)生的xxMapper對(duì)象仍然共享二級(jí)緩存。
- 禁用 :select標(biāo)簽中useCache="false"
清理:
1.與清理一級(jí)緩存的方法相同
commit(); (一般執(zhí)行增刪改時(shí) 會(huì)清理掉緩存;設(shè)計(jì)的原因 是為了防止臟數(shù)據(jù))
在二級(jí)緩存中,commit()不能是查詢自身的commit。
commit會(huì)清理一級(jí)和二級(jí)緩存;但是 清理二級(jí)緩存時(shí),不能是查詢自身的commit;
2. 在select標(biāo)簽中 增加屬性 flushCache="true"
命中率:緩存中存在就是命中。命中數(shù)/查詢數(shù)
三方提供的二級(jí)緩存:ehcache、memcache
要想整合三方提供的二級(jí)緩存 (或者自定義二級(jí)緩存),必須實(shí)現(xiàn)org.apache.ibatis.cache.Cache接口,該接口的默認(rèn)實(shí)現(xiàn)類是PerpetualCache
整合ehcache二級(jí)緩存:
- ehcache-core.jar mybatis-Ehcache.jar slf4j-api.jar
- 編寫ehcache配置文件 Ehcache.xml
- 開(kāi)啟EhCache二級(jí)緩存
在xxxMapper.xml中開(kāi)啟
<cache type="org.mybatis.caches.ehcache.EhcacheCache">
<!-- 通過(guò)property覆蓋Ehcache.xml中的值 -->
<property name="maxElementsInMemory" value="2000"/>
<property name="maxElementsOnDisk" value="3000"/>
</cache>