1. 什么是mybatis
MyBatis是一個優秀的持久層框架,它對jdbc的操作數據庫的過程進行封裝,使開發者只需要關注 SQL 本身,而不需要花費精力去處理例如注冊驅動、創建connection、創建statement、手動設置參數、結果集檢索等jdbc繁雜的過程代碼。
2. mybatis入門
2.1mybatis下載
mybaits的代碼由github.com管理,地址:https://github.com/mybatis/mybatis-3/releases
2.2工程搭建
第一步:創建java工程
第二步:加入需要的jar包
第三步:配置db.properties文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssm?characterEncoding=utf-8 jdbc.username=root jdbc.password=123456
第四步:編寫mybatyis配置文件SqlMapConfig.xml
為了看起來清晰,就把所有的配置文件放在一個新的文件夾中,所有在項目下新建一個文件夾config,把他設置為資源目錄(classpath,src也是資源目錄),然后新建一個SqlMapConfig.xml文件,編寫如下配置,不要忘了包掃描:
<?xml version="1.0" encoding="uTF-8" ?> <!-- mybatis核心配置 --> <!-- 導入約束的路徑 --> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- 配置信息 --> <configuration> <!-- 引入並加載外部文件 --> <properties resource="db.properties"></properties> <!-- 給類取別名 --> <typeAliases> <!-- 單獨取別名 <typeAlias type="com.entity.Admin" alias="admin"></typeAlias> --> <!-- 使用包掃描方式取別名 --> <package name="com.entity"/> </typeAliases> <!-- 環境配置的集合 --> <environments default="mysql"> <environment id="mysql"> <!-- 事務管理:type指定事務的管理方式,jdbc:交給jdbc管理,MANAGED:被管理 --> <transactionManager type="JDBC"></transactionManager> <!-- 數據庫配置:type是否使用連接池,POOLED:使用連接池,UNPOOLED:不使用連接池 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments> <!-- 加載映射文件 --> <mappers> <!-- 單獨加載映射文件 <mapper resource="adminMapper.xml"></mapper> --> <!-- 加載整個包 --> <package name="com.dao"/> </mappers> </configuration>
第五步:創建pojo實體類
package com.entity; public class User { private int id; private String username; private String password; //get和set,toString方法在此略,使用時需要生成 }
第六步:編寫mybatyis映射文件
新建一個userDao.xml文件,這個文件要和下面要創建的接口類放在同一個目錄。編寫如下配置:
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.dao.UserDao"> <!--查詢所有信息 id是這個select的唯一標識 resultType是返回類型 parameterType是參數類型 --> <select id="findAll" resultType="com.dao.User"> select * from user </select> </mapper>
第七步:創建一個接口
package com.dao; import com.entity.User; import java.util.List; public interface UserDao { List<User> findAll(); }
接口中的方法要和select的id相同,其他雷同,一定不要忘了在接口中寫對應的方法。
第八步:編寫測試類
在使用系統的測試類時,可以在測試目錄下新建一個另類,不能以Test命名,然后寫@Test導入junit的類,然后寫方法可以直接測試,不需要寫主方法:
package test; import com.entity.User; 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 org.apache.log4j.Logger; import org.junit.Test; import java.io.IOException; import java.io.Reader; import java.util.List; public class Mybatis02 { @Test public void findAll() throws IOException { //加載核心配置文件 Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml"); //創建SessionFactory SqlSessionFactory build= new SqlSessionFactoryBuilder().build(reader); //創建一個session對象 SqlSession session = build.openSession(); //操作映射 List<User> list = session.selectList("com.dao.UserDao.findAll"); System.out.println(list); } }
2.3自定義文件
在這里就自定義一個xml文件,使用起來方便點。
2.4配置日志文件
新建一個log4j.properties文件,配置如下:
### 設置當前日志級別 ### log4j.rootLogger = debug,stdout,D,E ### 輸出信息到控制抬 ### log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n ### 輸出DEBUG 級別以上的日志到=E://logs/log.log ### log4j.appender.D = org.apache.log4j.DailyRollingFileAppender log4j.appender.D.File = E://logs/log.log log4j.appender.D.Append = true log4j.appender.D.Threshold = DEBUG log4j.appender.D.layout = org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n ### 輸出ERROR 級別以上的日志到=E://logs/error.log ### log4j.appender.E = org.apache.log4j.DailyRollingFileAppender log4j.appender.E.File =E://logs/error.log log4j.appender.E.Append = true log4j.appender.E.Threshold = ERROR log4j.appender.E.layout = org.apache.log4j.PatternLayout log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
配置之后導入log4j的相關jar包,創建日志對象,把信息寫到日志文件中,盡量每天一個日志文件,把debug和error的日志寫到日志文件中,方便查閱:
//創建日志對象 Logger logger=Logger.getLogger(Mybatis02.class);
會在控制台輸出一些日志,這句代碼放在類中方法之前,debug信息會自動寫到文件中,error信息要在發生異常的地方手動添加。
catch (IOException e) { //把錯誤信息寫到日志文件 logger.error(e.getMessage()); e.printStackTrace(); }
3. mybatis的增刪改查
這里有多個方法,所以進行代碼優化:
public SqlSessionFactory build=null; //這個在方法加載的時候就會初始化 @Before public void before(){ Logger logger=Logger.getLogger(Mybatis02.class); Reader reader=null; //加載核心配置文件 try { reader= Resources.getResourceAsReader("SqlMapConfig.xml"); //創建SessionFactory build = new SqlSessionFactoryBuilder().build(reader); } catch (IOException e) { e.printStackTrace(); } }
3.1根據id查詢
<!-- 如果參數是簡單數據類型,可以省略 --> <select id="findById" parameterType="int" resultType="User"> select * from user where id=#{id} </select>
接口中的方法比較簡單,這里就不展示,不要忘了編寫對應的方法
@Test public void findById(){ SqlSession session = build.openSession(); User user=session.selectOne("com.dao.UserDao.findById",14); System.out.println(user); }
com.dao.UserDao.findById代表的是接口中的方法,后面接參數
3.2查詢所有
<select id="findAll" resultType="com.entity.User"> select * from user </select>
@Test public void findAll() throws IOException { //創建一個session對象 SqlSession session = build.openSession(); //操作映射 List<User> list = session.selectList("com.dao.UserDao.findAll"); System.out.println(list); }
3.3根據用戶名和密碼查詢
<select id="findByNmAndPwd" parameterType="User" resultType="User"> select * from user where username=#{username} and password=#{password} </select>
#{ }里面的參數一定要和對象中的屬性保持一致
@Test public void findByNmAndPwd(){ SqlSession session = build.openSession(); User user=new User(); user.setUsername("方啟豪"); user.setPassword("123"); User user2 = session.selectOne("com.dao.UserDao.findByNmAndPwd", user); System.out.println(user2); }
3.4添加用戶
<insert id="addUser" parameterType="User"> insert into user values(null,#{username},#{password}) </insert>
增加刪除修改一定要提交事務
@Test public void addUser(){ SqlSession session = build.openSession(); User user=new User(); user.setUsername("方啟豪5號"); user.setPassword("12315"); int i = session.insert("com.dao.UserDao.addUser", user); session.commit();//提交事務 System.out.println(i); }
3.5修改用戶
<update id="updateUser" parameterType="com.entity.User"> update user set username=#{username},password=#{password} where id=#{id} </update>
@Test public void updateUser(){ SqlSession session = build.openSession(); User user=new User(); user.setId(9); user.setUsername("方啟豪2號"); user.setPassword("123"); session.insert("com.dao.UserDao.updateUser", user); session.commit(); }
3.6刪除用戶
<delete id="deleteUser"> delete from user where id=#{value} </delete>
@Test public void deleteUser(){ SqlSession session = build.openSession(); session.delete("com.dao.UserDao.deleteUser", 9); session.commit(); }
3.7模糊查詢:一個條件
使用${},並且括號里面只能寫value,這里參數是簡單數據類型
<!-- 模糊查詢:一個條件,使用${},並且括號里面只能寫value -->
<select id="findByLikeOne" resultType="User">
select * from user where username like '%${value}%'
</select>
@Test public void findByLikeOne(){ SqlSession session = build.openSession(); List<User> list=session.selectList("com.dao.UserDao.findByLikeOne", "啟"); System.out.println(list); }
3.8模糊查詢:多個條件
使用${},並且括號里面要與對象的屬性相同
<!-- 模糊查詢:多個條件,使用${},並且括號里面要與對象的屬性相同 -->
<select id="findByLikeMore" parameterType="User" resultType="User">
select * from user where username like '%${username}%' and password like '%${password}%'
</select>
@Test public void findByLikeMore(){ SqlSession session = build.openSession(); User user=new User(); user.setUsername("鍾"); user.setPassword("1"); List<User> list=session.selectList("com.dao.UserDao.findByLikeMore", user); System.out.println(list); }
4. 小結
4.1#{}和${}的區別
#{}表示一個占位符號,通過#{}可以實現preparedStatement向占位符中設置值,自動進行java類型和jdbc類型轉換,#{}可以有效防止sql注入。 #{}可以接收簡單類型值或pojo屬性值。 如果parameterType傳輸單個簡單類型值,#{}括號中可以是value或其它名稱。
${}表示拼接sql串,通過${}可以將parameterType 傳入的內容拼接在sql中且不進行jdbc類型轉換, ${}可以接收簡單類型值或pojo屬性值,如果parameterType傳輸單個簡單類型值,${}括號中只能是value。
4.2parameterType和resultType
parameterType:指定輸入參數類型
resultType:指定輸出結果類型
4.3selectOne和selectList
selectOne查詢一條記錄,如果使用selectOne查詢多條記錄則拋出異常:
org.apache.ibatis.exceptions.TooManyResultsException
selectList可以查詢一條或多條記錄
4.4mybatis與hibernate的區別
Mybatis是對jdbc的封裝,注重的是sql語句,是一種輕量級的半自動框架,適用於敏捷開發,原理是反射
Hibernate注重映射關系,是一種重量級的全自動的框架。
4.5取別名typeAliases
mybatis支持別名:
別名 |
映射的類型 |
_byte |
byte |
_long |
long |
_short |
short |
_int |
int |
_integer |
int |
_double |
double |
_float |
float |
_boolean |
boolean |
string |
String |
byte |
Byte |
long |
Long |
short |
Short |
int |
Integer |
integer |
Integer |
double |
Double |
float |
Float |
boolean |
Boolean |
date |
Date |
decimal |
BigDecimal |
bigdecimal |
BigDecimal |
map |
Map |
自定義別名:
在SqlMapConfig.xml中配置:
<typeAliases> <!-- 單個別名定義 --> <typeAlias alias="user" type="com.entity.User"/> <!-- 批量別名定義,掃描整個包下的類,別名為類名(首字母大寫或小寫都可以) --> <package name="com.entity"/> <package name="其它包"/> </typeAliases>
4.6mappers(映射器)
Mapper配置的幾種方法:
第一種:<mapper resource=" " />使用相對於類路徑的資源
如:<mapper resource="sqlmap/User.xml" />
第二種:<mapper class=" " />使用mapper接口類路徑
如:<mapper class="com.mapper.UserMapper"/>
注意:此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中。
第三種:<package name=""/>注冊指定包下的所有mapper接口
如:<package name="com.mapper"/>
注意:此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中。
<!-- 加載映射文件 --> <mappers> <!-- 單獨加載映射文件 <mapper resource="adminMapper.xml"></mapper> --> <!-- 加載整個包 --> <package name="com.dao"/> </mappers>
5. dao開發
5.1原始dao開發
原始Dao開發方法需要程序員編寫Dao接口和Dao實現類。
1、編寫映射文件:
<?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="test"> <!-- 根據id獲取用戶信息 --> <select id="findUserById" parameterType="int" resultType="com.hp.entity.User"> select * from user where id = #{id} </select> <!-- 根據name模糊查詢 --> <select id="findLikeName" parameterType="java.lang.String" resultType="com.hp.entity.User"> select * from user where username like '%${value}%' </select> <!-- 添加用戶 --> <insert id="insertUser" parameterType="com.hp.entity.User"> <!-- 獲取生成的主鍵的值,返回到user對象中 --> <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> select LAST_INSERT_ID() </selectKey> insert into user(username,password) values(#{username},#{password}) </insert> </mapper>
2、編寫dao文件:
接口:
public interface IUserDao { User findUserById(int id); List<User> findUserLikeName(String name); void addUser(User user); }
實現類:
public class UserDaoImpl implements IUserDao{ SqlSessionFactory factory = null; public UserDaoImpl(SqlSessionFactory factory) { this.factory = factory; } @Override public User findUserById(int id) { SqlSession session = factory.openSession(); User user = session.selectOne("test.findById", id); session.close(); return user; } @Override public List<User> findUserLikeName(String name) { SqlSession session = factory.openSession(); List<User> list = session.selectList("test.findLikeName", name); session.close(); return list; } @Override public void addUser(User user) { SqlSession session = factory.openSession(); session.insert("test.insertUser", user); session.commit(); session.close(); } }
3、測試:
public class MybatisTest { SqlSessionFactory factory = null; @Before public void createFactory() throws IOException{ InputStream is = Resources.getResourceAsStream("sqlMapConfig.xml"); factory = new SqlSessionFactoryBuilder().build(is); } @Test public void test7(){ IUserDao dao = new UserDaoImpl(factory); User user = dao.findUserById(4); System.out.println(user); } @Test public void test8(){ IUserDao dao = new UserDaoImpl(factory); List<User> list = dao.findUserLikeName("李"); System.out.println(list); } @Test public void test9(){ IUserDao dao = new UserDaoImpl(factory); User user = new User(); user.setPassword("2222"); user.setUsername("張三"); dao.addUser(user); }
5.2Mapper動態代理方式開發Dao
只需要程序員編寫Mapper接口,非常適用於mybatis,Mapper接口開發需要遵循以下規范:
1、Mapper.xml文件中的namespace與mapper接口的路徑相同。
2、Mapper接口方法名和Mapper.xml中定義的每個statement的id相同
3、Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同
4、Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同
配置文件及測試在入門里已經進行了,這里就省略了。動態代理對象調用sqlSession.selectOne()和sqlSession.selectList()是根據mapper接口方法的返回值決定,如果返回list則調用selectList方法,如果返回單個對象則調用selectOne方法。
//mapper動態代理模板
@Test public void testInsert() { SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); Emp emp = new Emp(); emp.setEname("admin"); emp.setJob("admin"); int flag = 0; try { flag = empMapper.insertEmp(emp); }finally { sqlSession.commit(); sqlSession.close(); } if(flag == 1) { System.out.println("自增主鍵值:"+emp.getEmpno()); } }
7.resultMap與parameterMap
7.1復雜條件的查詢舉例
當參數不屬於同一個對象時,使用簡單的查詢非常麻煩,那么可以把一個對象作為另一個對象的屬性結合使用:
package com.entity; public class Person { private String username; } package com.entity; public class People { private int age; }
把上面的兩個對象作為屬性給PVo,那么他使用起來就比較方便了。
package com.entity; public class PVo { private People people; private Person person; }
一個簡單的插入語句:
<insert id="Pvo" parameterType="PVo"> insert into user valuesnull,#{person.username},#{people.password}) </insert>
7.2resultMap的使用
當查詢的結果不僅僅是一個對象的全部屬性時,如果再創建一個對象來封裝數據就顯得格外的麻煩,那么可以使用resultMap來對屬性進行重定義,這樣就可以直接返回一個map的結果。
復雜查詢:查詢用戶的id,用戶名以及所對應的成績的id和具體成績(一對一)
數據庫信息
<!-- 復雜查詢:查詢用戶的id,用戶名以及所對應的成績的id和具體成績 --> <!--定義表數據與組合類的映射關系,id是唯一標識,type是類型--> <resultMap id="userAndScore" type="User"> <!--id是主鍵映射,result是非主鍵映射,column是數據庫中的列名,properties是對象的屬性名--> <!--如果多個表中的屬性名一樣,要注意區分,如這里的id--> <id column="uid" property="id"></id> <result column="username" property="username"></result> <result column="password" property="password"></result> <!--association對屬性是對象的變量的深入映射,適用於一對一的關系--> <association property="score" javaType="Score"> <id column="sid" property="id"></id> <result column="grade" property="grade"></result> <result column="u_id" property="u_id"></result> </association> </resultMap> <!--這里的返回類型必須是map集合--> <select id="findUserAndScore" resultMap="userAndScore"> select user.id uid, username,score.id sid,grade from user,score where user.id=score.u_id </select>
User類
package com.entity; import lombok.Getter; import lombok.Setter; import lombok.ToString; @Getter @Setter @ToString public class User { private int id; private String username; private String password; //score的類型是對象 private Score score; } Score類 package com.entity; import lombok.Getter; import lombok.Setter; import lombok.ToString; @Getter @Setter @ToString public class Score { private int id; private double grade; private int u_id; }
測試方法
@Test public void findUserAndScore(){ SqlSession session = build.openSession(); List<User> list=session.selectList("com.dao.UserDao.findUserAndScore"); System.out.println(list); }
復雜查詢:查詢用戶的id,用戶名以及所對應的成績的id和具體成績(一對多)
數據庫信息
<resultMap id="userAndScore" type="User"> <id column="uid" property="id"></id> <result column="username" property="username"></result> <result column="password" property="password"></result> <!--collection對屬性是對象的變量的深入映射,適用於一對多的關系--> <!--javaType指屬性是什么類型,ofType指集合中裝的什么類型--> <collection property="scores" javaType="java.util.List" ofType="Score"> <id column="sid" property="id"></id> <result column="grade" property="grade"></result> <result column="u_id" property="u_id"></result> </collection> </resultMap> <!--這里的返回類型必須是map集合--> <select id="findUserAndScore" resultMap="userAndScore"> select user.id uid, username,score.id sid,grade from user,score where user.id=score.u_id </select>
User類如下,score和測試方法沒有變。
8.動態sql
8.1if標簽
當標簽里面test的內容為true時才會去執行if里面的語句
查詢:根據id查詢,如果id不為空就根據id查詢,否則就讓id為0進行查詢
<select id="findById" resultType="User"> select * from user <if test="value!=null"> where id=#{value} </if> <if test="value==null"> where id=0 </if> </select>
8.2where標簽
where會自動去識別,當sql語句中已經有where那么它會直接拼接where里面的內容,如果沒有就直接使用;另外where里面的and也類似,如果已經有where就使用and,如果沒有就舍棄第一個and。
查詢:當id不為空就根據id查詢,否則就查詢所有
<select id="findById" resultType="User"> select * from user where 1=1 <if test="value!=null"> <where> and id=#{value} </where> </if> </select>
8.3foreach標簽
用來遍歷集合等,用於傳入參數是多個相同類型的查詢,可以使用在in,limit,between。。。
查詢:傳入多個id查詢用戶信息
<select id="findByIn" parameterType="java.util.List" resultType="User"> select * from user <!--如果傳入參數是list類型,必須使用list獲取,並且使用and--> <if test="list.size()>0 and list!=null"> where id in <!--collection要遍歷的集合,open開始的符號,close結束的符號,separator分隔符--> <!--index是索引,item是每一條內容,這里是list.get(i)--> <foreach collection="list" open="(" separator="," close=")" index="i" item="item" > #{item} </foreach> </if> </select>
@Test public void findByIn(){ SqlSession session = build.openSession(); List<Integer> list=new ArrayList<Integer>(); list.add(1); list.add(14); list.add(22); List<User> list1=session.selectList("com.dao.UserDao.findByIn",list); System.out.println(list1); }
8.4sql片段
將會重復使用的sql語句抽取出來,需要使用的地方直接引入
<!--sql片段--> <sql id="select"> select * from user </sql> <select id="findById" resultType="User"> <!--引入sql片段--> <include refid="select"/> where is=#{value} </select>
9.延遲加載
需要查詢關聯信息時,我們先查詢一部分信息,再關聯查詢關聯信息,叫延遲加載。主要通過resultMap實現延遲加載。延遲加載需要導入cglib以及asm的jar包。
9.1打開延時加載的開關
在SqlMapConfig.xml中配置:
<settings> <!-- 延遲加載開關 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 當設置為true的時候,懶加載的對象可能被任何懶屬性全部加載。否則,每個屬性都按需加載--> <setting name="aggressiveLazyLoading" value="false"/> </settings>
9.2寫sql語句
根據用戶名和密碼查詢出id,然后再根據id查詢對應的成績
User類和Score類在此略,可參考上面7.2復雜查詢中一對多查詢的定義
9.3測試
@Test public void findUserAndScore2(){ SqlSession session = build.openSession(); User user=new User(); user.setUsername("鍾玉石"); user.setPassword("123"); List<User> list=session.selectList("com.dao.UserDao.findUserAndScore2",user); System.out.println(list.get(0).getId()); System.out.println("-------------"); System.out.println(list.get(0).getScores().get(0)); }
查詢之后可以看到先執行一次查詢,然后再去執行后面的查詢,並沒有一次性查詢出結果。
9. Mybatis緩存
將從數據庫查詢的數據存儲到內存中緩存起來,這樣就不用從數據庫中查詢數據而從緩存中查詢,提高查詢的速度,減少對數據庫的訪問。
10.1一級緩存
一級緩存使用
一級緩存:當執行兩條相同的查詢語句時,mybatis只執行一次查詢,map中存儲了sql執行查詢的結果集。
一級緩存是session級別的緩存,當執行增刪改中的任何一個操作,緩存就會清空。
一級緩存查詢:
@Test public void findById(){ SqlSession session = build.openSession(); User user=session.selectOne("com.dao.UserDao.findById",14); System.out.println(user); System.out.println("----------"); User user2=session.selectOne("com.dao.UserDao.findById",14); System.out.println(user2); }
查詢結果如下
一級緩存查詢中執行增加操作:
@Test public void findById(){ SqlSession session = build.openSession(); User user=session.selectOne("com.dao.UserDao.findById",14); System.out.println(user); User user3=new User(); user3.setUsername("方啟豪6號"); user3.setPassword("12315"); session.insert("com.dao.UserDao.addUser", user3); session.commit();//提交事務 System.out.println("----------"); User user2=session.selectOne("com.dao.UserDao.findById",14); System.out.println(user2); }
只要清空了session,那么就清除了緩存,就需要重新查詢。也可以手動清空session,語句是
一級緩存原理
第一次查詢先去緩存中找是否有緩存數據,發現沒有,查詢數據庫,將查詢到的數據寫入sqlsession的一級緩存區域。
第二次查詢先去緩存中找是否有緩存數據,發現有,直接從緩存區域中取出數據返回。如果執行sqlsession的添加、修改、刪除等操作,會執行commit,最終會清空緩存。
10.2二級緩存
二級緩存是mapper(命名空間)級別的緩存,默認是開啟的,只有session提交或關閉時才能把結果提交到二級緩存中,當執行增刪改中的任何一個操作,緩存就會清空。
二級緩存的使用
二級緩存的使用需要進行配置:
第一步:在SqlMapConfig.xml的settings中加一行代碼:
<!--開啟並標識二級緩存--> <setting name="cacheEnabled" value="true"/>
第二步:在userDao.xml中加一行代碼:
第三步:User類要實現Serializable接口
二級緩存測試
@Test public void findById(){ SqlSession session = build.openSession(); SqlSession session2 = build.openSession(); UserDao mapper=session.getMapper(UserDao.class); UserDao mapper2=session2.getMapper(UserDao.class); User user=mapper.findById(14); System.out.println(user); session.close(); System.out.println("----------"); User user2=mapper2.findById(14); System.out.println(user2); session2.close(); }
查詢結果
二級緩存原理
不同的sqlsession都要調用mapper下的sql語句發起數據庫請求。sqlsession1執行UserMapper下的查詢用戶請求先從二級緩存中查找有沒有數據,如果沒有就從數據庫中查詢,並且將查詢到數據存儲二級緩存中。
sqlsession2執行UserMapper下的同一個查詢用戶請求,先從二級緩存中查找有沒有數據,如果有就從二級緩存中查詢數據,返回。
如果有一個sqlsession3執行UserMapper下添加、修改、刪除語句,執行commit操作后,將UserMapper下的所有緩存數據全部清空。
9. Mybatis注解
11.1@Param參數傳入
當參數少於4個的時候可以使用注解參數的傳入,如果參數過多,就使用原始pojo的方式
UserDao類:
//使用注解Param傳遞參數,引號里面的參數是要使用的參數,如#{username} User findByNmAndPwd(@Param("username")String username,@Param("password")String password);
userDao.xml中sql:
<select id="findByNmAndPwd" resultType="User"> select * from user where username=#{username} and password=#{password} </select>
測試類:
@Test public void findByNmAndPwd(){ SqlSession session = build.openSession(); UserDao mapper = session.getMapper(UserDao.class); User user=mapper.findByNmAndPwd("方啟豪","123"); System.out.println(user); }
11.2@Insert @Update @Delete
此注解不需要userDao.xml文件,這三個用法雷同,以update為例:
UserDao類:
//這里參數傳的是一個對象 @Update("update user set username=#{user.username}, password=#{user.password} where id=#{user.id}") void updateUser(@Param("user")User user);
測試類:
@Test public void updateUser(){ SqlSession session = build.openSession(); UserDao mapper = session.getMapper(UserDao.class); User user=new User(); user.setId(14); user.setUsername("方啟豪222號"); user.setPassword("123"); mapper.updateUser(user); session.commit(); }
11.3@Select
此注解不需要userDao.xml文件
UserDao類:
@Select("select * from user where id=#{id}") User findById(@Param("id") Integer id);
測試類:
Test public void findById(){ SqlSession session = build.openSession(); UserDao mapper=session.getMapper(UserDao.class); User user=mapper.findById(14); System.out.println(user); }
12.異常問題
1.Mybatis異常 There is no getter for property...
在dao層修改:
List<Article> recommandList(@Param("siteid") Integer siteid);
如上修改,給siteid @Param注入getter 即可。
總結:如果xml的parameterType屬性時Map,則不需要加@Param。如果是一個int,string這種類型的參數,需要在Dao層加@Param注解;只有一個參數,xml不用if,可不加。有多個參數,dao層每個參數都要加上@Param。如果傳入的參數特別多,建議直接傳入一個Map。