一、簡介:
- iBATIS一詞來源於“internet”和“abatis”的組合,是一個基於Java的持久層框架。iBATIS提供的持久層框架包括SQL Maps和Data Access Objects(DAO)
-
MyBatis的前身就是iBatis,iBatis本是由ClintonBegin開發,後來捐給Apache基金會,成立了iBatis開源項目。2010年5月該項目由Apahce基金會遷移到了GoogleCode,並且改名爲MyBatis。儘管如此,它的包結構仍然爲ibatis。
-
訪問網站www.mybatis.org/https://github.com/mybatis
-
MyBatis是一個數據持久層(ORM)框架。把實體類和SQL語句之間建立了映射關係,是一種半自動化的ORM實現。
-
所有sql語句,全部定義在xml(建議)中。也可以通過註解的方式在接口上實現。這些映射文件稱之爲mapper。
二、MyBatis的優點(與其他架構區別)
- MyBatis的優點:
- 基於SQL語法,簡單易學。
- 能瞭解底層組裝過程。
- SQL語句封裝在配置文件中,便於統一管理與維護,降低了程序的耦合度。
- 程序調試方便。
- 與傳統JDBC的比較
-
減少了61%的代碼量
-
最簡單的持久化框架
-
架構級性能增強
-
SQL代碼從程序代碼中徹底分離,可重用
-
增強了項目中的分工
-
增強了移植性
-
- 與Hibernate的對比
-
MyBatis和Hibernate比較 MyBatis Hibernate
是一個SQL語句映射的框架(工具) 主流的ORM框架、提供了從POJO到數據庫表的全套映射機制 注重POJO/Map與SQL之間的映射關係。不會爲程序員在運行期自動生成SQL 會自動生成全套SQL語句。 自動化程度低、手工映射SQL,靈活程度高 因爲自動化程度高、映射配置複雜,api也相對複雜,靈活性低. 需要開發人員熟煉掌據SQL語句 開發人同不必關注SQL底層語句開發
三、架構
1.功能架構講解:
我們把Mybatis的功能架構分爲三層:
- API接口層:提供給外部使用的接口API,開發人員通過這些本地API來操縱數據庫。接口層一接收到調用請求就會調用數據處理層來完成具體的數據處理。
- 數據處理層:負責具體的SQL查找、SQL解析、SQL執行和執行結果映射處理等。它主要的目的是根據調用的請求完成一次數據庫操作。
- 基礎支撐層:負責最基礎的功能支撐,包括連接管理、事務管理、配置加載和緩存處理,這些都是共用的東西,將他們抽取出來作爲最基礎的組件。爲上層的數據處理層提供最基礎的支撐。
2.框架架構講解:
-
加載配置:配置來源於兩個地方,一處是配置文件,一處是Java代碼的註解,將SQL的配置信息加載成爲一個mybatis結構個MappedStatement對象(包括了傳入參數映射配置、執行的SQL語句、結果映射配置),存儲在內存中。
-
SQL解析:當API接口層接收到調用請求時,會接收到傳入SQL的ID和傳入對象(可以是Map、JavaBean或者基本數據類型),Mybatis會根據SQL的ID找到對應的MappedStatement,然後根據傳入參數對象對MappedStatement進行解析,解析後可以得到最終要執行的SQL語句和參數。
-
SQL執行:將最終得到的SQL和參數拿到數據庫進行執行,得到操作數據庫的結果。
-
結果映射:將操作數據庫的結果按照映射的配置進行轉換,可以轉換成HashMap、JavaBean或者基本數據類型,並將最終結果返回。
四、MyBatis基本要素
somename.xml全局配置文件
mapper.xml核心映射文件
SqlSession接口
1.基礎配置文件mybatis-config.xml
六、基本使用演示:
1.基礎配置文件mybatis-config.xml
-
configuration.xml是系統的核心配置文件,包含數據源和事務管理器等設置和屬性信息,XML文檔結構如下:
configuration配置properties可以配置在Java屬性配置文件中settings修改MyBatis在運行時的行爲方式,如是否需要緩存typeAliases 爲Java類型命名一個短的名字typeHandlers類型處理器-系統默認已經爲所有類型設置OKobjectFactory對象工廠–創建Bean。plugins插件 - 攔截CRUD操作。environments環境 -配置數據源environment 環境變量transactionManager事務管理器 JDBC|JNDI|JTAdataSource數據源mappersl 映射器 XML文件|Mapper類|網絡資源
- 基礎配置文件—環境配置
配置環境
<span style="font-size:12px;"><configuration> <environmentsdefault="development"> <environmentid="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <propertyname="driver" value="${driver}"/> <property name="url" value="${url}"/> <propertyname="username" value="${username}"/> <propertyname="password" value="${password}"/> </dataSource> </environment> <environmentid="development2"> …… </environment> </environments> </configuration></span>
- 基礎配置文件—事務管理
MyBatis有兩種事務管理類型:
lJDBC- 這個類型直接全部使用JDBC的提交和回滾功能。它依靠使用連接的數據源來管理事務的作用域。lMANAGED- 這個類型什麼不做, 它從不提交 、 回滾和關閉連接 。 而是讓窗口來管理事務的全部生命週期 。(比如說 Spring或者JAVAEE服務器)
- 基礎配置文件—數據源
數據源類型有三種:UNPOOLED,POOLED,JNDI。
UNPOOLED- 這個數據源實現只是在每次請求的時候簡單的打開和關閉一個連接。雖然這有點慢,但作爲一些不需要性能和立即響應的簡單應用來說, 不失爲一種好選擇 。
POOLED- 這個數據源緩存JDBC連接對象用於避免每次都要連接和生成連接實例而需要的驗證時間。對於併發WEB應用,這種方式非常流行因爲它有最快的響應時間。
JNDI- 這個數據源實現是爲了準備和Spring或應用服務一起使用,可以在外部也可以在內部配置這個數據源,然後在JNDI上下文中引用它。這個數據源配置只需要兩上屬性:
- 基礎配置文件—SQL映射文件
SQL映射文件:
1.使用相對路徑
<mappers> <mapper resource="org/mybatis/builder/UserMapper.xml"/> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
//2.使用全路徑<mappers>
<mapper url="file:///var/sqlmaps/AuthorMapper.xml"/>
<mapper url="file:///var/sqlmaps/BlogMapper.xml"/>
<mapper url="file:///var/sqlmaps/PostMapper.xml"/>
</mappers>
2.SQL映射文件
- SQL映射文件結構:(有關文件的增刪改查結構將用代碼來演示)
cache- 配置給定命名空間的緩存。
cache-ref– 從其他命名空間引用緩存配置。
resultMap – 最複雜,也是最有力量的元素,用來描述如何從數據庫結果集中來加載對象。l
sql– 可以重用的SQL塊,也可以被其他語句引用。
insert– 映射插入語句
update– 映射更新語句
delete– 映射刪除語句
select– 映射查詢語句
- 動態SQL
MyBatis的一個強大的特性之一通常是它的動態SQL能力ifchoose(when,otherwise)trim(where,set)foreach
五、核心類的生命週期
- SqlSessionFactoryBuilder的生命週期:這個類可以被初始 、 使用和丟棄 , 如果你已經創建好了一個SqlSessionFactory後就不用再保留它。 因此 ,SqlSessionFactoryBuilder的最好作用域是方法體內,比如說定義一個方法變量。你可以重複使用SqlSessionFactoryBuilder生成多個SqlSessionFactory實例, 但是最好不要強行保留 , 因爲 XML 的解析資源要用來做其它更重要的事
- SqlSession 每個線程都有自己的SqlSession實例,SqlSession實例是不能被共享,也是不是線程安全的。因此最好使用Request作用域或者方法體作用域。不要使用類的靜態變量來引用一個SqlSession實例,甚至不要使用類的一個實例變更來引用。如果你正在使用WEB框架,應該讓SqlSession跟隨HTTP請求的相似作用域。也就是說,在收到一個HTTP請求過後,打開SqlSession,等返回一個迴應以後,立馬關掉這個SqlSession。關閉SqlSession是非常重要的。你必須要確保SqlSession在finally方法體中正常關閉。
- SqlSession 的獲取:
package cn.hncu.utils; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; 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 SqlSessionUtil { private static SqlSessionFactory ssf;// 底層對應的是Connection連接池 static { InputStream in; try { in = Resources.getResourceAsStream("mybatis-config.xml");// 從classpath位置加載文件,可以使用相對路徑如:cn/hncu/mybatis-config.xml SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder(); ssf=ssfb.build(in); } catch (IOException e) { e.printStackTrace(); } } public static SqlSessionFactory getSsf() { return ssf; } public static SqlSession getSqlSession() { return ssf.openSession(); } public static void main(String[] args) throws SQLException { //SqlSession是Connection的包裝類 //每次獲取的是不同的SqlSession對象,如果獲取一個session對象且從中訪問con,則就會到池中去拿一個連接。由於我們在配置文件中設置池大小隻有5,因此只有前5個session對象能夠順利取出con對象,第6個session在獲取con時會阻塞。 //一個session對象在獲取con之後如果不使用,那麼超過默認時間,它所持有的con連接會被收回池中。這樣其它session對象又可以從池中獲取 System.out.println("ssf:"+ssf); //下面這段代碼,在5個session獲取連接之後會阻塞。過一段時間後,才繼續執行6-10個session對象 for(int i=1;i<=10;i++){ SqlSession ss=getSqlSession(); System.out.println(i+":sqlSession:"+ss);//sqlSession不同 Connection con=ss.getConnection(); System.out.println(i+":con:"+con);//Connection相同 ss.close();//session對象關閉之後,它所持有的con會還回池中,被下一個session重新持有 } } }
mybatis-config.xml的配置是基礎的配置
-
SqlSession接口主要方法
新增:intinsert(String statement, Object parameter)
修改:intupdate(String statement, Object parameter)
刪除:intdelete(String statement, Object parameter)
查詢:List selectList(String statement, Object parameter)
六、基本使用演示:
##使用前準備:
1).導入log4j-1.2.16.jar,mybatis-3.4.1.jar和mysql-connector-java-5.1.34-bin.jar包
2).在src目錄下導入或編寫mybatis-config.xml配置文件(文件名可以自己取)
<?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>
<typeAliases>
<typeAlias alias="User" type="cn.hncu.domain.User" />
<typeAlias alias="Person" type="cn.hncu.domain.Person" />
<!-- <typeAlias alias="User" type="cn.hncu.domain.UserMapper" /> 如果用namespace不行-->
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql:///mybatis?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="1234" />
<property name="poolMaximumActiveConnections" value="5" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="cn/hncu/domain/User.xml" />
<mapper resource="cn/hncu/domain/User2.xml" />
<mapper resource="cn/hncu/domain/Person.xml" />
</mappers>
</configuration>
3).寫SqlSessionUtil連接池工具。(用於拿連接)
package cn.hncu.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
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 SqlSessionUtil {
private static SqlSessionFactory ssf;// 底層對應的是Connection連接池
static {
InputStream in;
try {
in = Resources.getResourceAsStream("mybatis-config.xml");// 從classpath位置加載文件,可以使用相對路徑如:cn/hncu/mybatis-config.xml
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
ssf=ssfb.build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSsf() {
return ssf;
}
public static SqlSession getSqlSession() {
return ssf.openSession();
}
public static void main(String[] args) throws SQLException {
//SqlSession是Connection的包裝類
//每次獲取的是不同的SqlSession對象,如果獲取一個session對象且從中訪問con,則就會到池中去拿一個連接。由於我們在配置文件中設置池大小隻有5,因此只有前5個session對象能夠順利取出con對象,第6個session在獲取con時會阻塞。
//一個session對象在獲取con之後如果不使用,那麼超過默認時間,它所持有的con連接會被收回池中。這樣其它session對象又可以從池中獲取
System.out.println("ssf:"+ssf);
//下面這段代碼,在5個session獲取連接之後會阻塞。過一段時間後,才繼續執行6-10個session對象
for(int i=1;i<=10;i++){
SqlSession ss=getSqlSession();
System.out.println(i+":sqlSession:"+ss);//sqlSession不同
Connection con=ss.getConnection();
System.out.println(i+":con:"+con);//Connection相同
ss.close();//session對象關閉之後,它所持有的con會還回池中,被下一個session重新持有
}
}
}
4)在mybatis數據庫中建立表格(利用ant工具)
demo.sql
create database if not exists mybatis default character set 'utf8';
use mybatis;
create table users(
id varchar(32) primary key,
name varchar(32),
pwd varchar(32)
);
insert into users value('U001','Jack','1234');
insert into users value('U010','張三','4321');
CREATE TABLE cpu(
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(30)
);
CREATE TABLE persons(
pid VARCHAR(32) PRIMARY KEY,
pname VARCHAR(30)
);
CREATE TABLE cars(
id VARCHAR(32) PRIMARY KEY,
NAME VARCHAR(30),
price NUMERIC(10,2),
pid VARCHAR(32),
CONSTRAINT car_fk FOREIGN KEY(pid) REFERENCES persons(pid)
);
INSERT INTO persons VALUES('P001','Jack');
INSERT INTO persons VALUES('P002','Rose');
INSERT INTO persons VALUES('P003','張三');
INSERT INTO cars VALUES('C001','BMW',100,'P001');
INSERT INTO cars VALUES('C002','BenZ',80,'P001');
INSERT INTO cars VALUES('C003','Jeep',50,'P003');
CREATE TABLE card(
card_id VARCHAR(32) PRIMARY KEY,
card_gov VARCHAR(30),
pid VARCHAR(32) unique,
CONSTRAINT card_fk FOREIGN KEY(pid) REFERENCES persons(pid)
);
INSERT INTO card VALUES('C001','北京市公安局','P001');
INSERT INTO card VALUES('C002','長沙市公安局','P002');
CREATE TABLE roles(
id VARCHAR(32) PRIMARY KEY,
name VARCHAR(30),
);
CREATE TABLE roleuser(
roleid VARCHAR(32),
userid VARCHAR(32),
CONSTRAINT ru_pk PRIMARY KEY(roleid,userid),
CONSTRAINT ru_fk1 FOREIGN KEY(roleid)
REFERENCES roles(id),
CONSTRAINT ru_fk1 FOREIGN KEY(userid)
REFERENCES users(id)
);
INSERT INTO roles VALUES('R001','教師');
INSERT INTO roles VALUES('R002','老闆');
INSERT INTO roles VALUES('R003','學生');
INSERT INTO roleuser VALUES('R001','U001');
INSERT INTO roleuser VALUES('R002','U001');
INSERT INTO roleuser VALUES('R003','U001');
INSERT INTO roleuser VALUES('R001','U010');
INSERT INTO roleuser VALUES('R001','U002');
INSERT INTO roleuser VALUES('R002','U003');
INSERT INTO roleuser VALUES('R002','U003');
5)使用。(建立javaBean)
1.查詢:
1)查詢學生信息
映射文件<!--查詢的返回結果是一個集合(List),resultType屬性設置的是集合中一個元素的類型--> <select id="query1" resultType="User" ><!--用別名來設置返回類型--> select * from users </select>
測試代碼:2)查詢學生信息(接口方式---推薦方式)@Test//查詢學生信息 public void query1(){ SqlSession s=SqlSessionUtil.getSqlSession(); List<User> list=s.selectList("query1"); System.out.println(list); s.close();//執行之後,最好養成關session的習慣 }
映射文件接口代碼<!--採用接口的方式訪問時,命名空間得取成“接口全名” --> <mapper namespace="cn.hncu.domain.UserMapper"> <!--查詢的返回結果是一個集合(List),resultType屬性設置的是集合中一個元素的類型--> <select id="query1" resultType="User" ><!--用別名來設置返回類型--> select * from users </select> </mapper>
package cn.hncu.domain; import java.util.List; public interface UserMapper { public List<User> query1(); public List<User> queryByID(String string); }
測試代碼@Test//採用接口的方式訪問----mybatis推薦的訪問方式 public void queryByInterface(){ SqlSession s=SqlSessionUtil.getSqlSession(); //與上一版本不同之處---通過接口訪問 UserMapper up=s.getMapper(UserMapper.class); List<User> list=up.query1(); System.out.println(list); s.close(); }
3)查詢學生信息(帶單個條件)
映射文件測試代碼<!--帶條件的查詢1:單個條件 助理解:類型方法中的形參:String val --> <select id="queryByID" resultType="User" parameterType="string"> select * from users where 1=1 and id=#{val} <!-- <if test="id!=null"> and id=#{val} </if> 不行--> </select>
@Test//帶條件的查詢1:單個條件 public void queryByID(){ SqlSession s=SqlSessionUtil.getSqlSession(); //與上一版本不同之處---通過接口訪問 List<User> list=s.selectList("queryByID", "U001"); System.out.println(list); s.close(); }
4)帶多條件的查詢2:多個條件
映射文件<!--帶條件的查詢2:多個條件 注意: sql語句中的參數名必須和值對象中的屬性名一致 --> <select id="queryUser" resultType="User" parameterType="User"> select * from users where 1=1 and id=#{id} <if test="name!=null"> and name like #{name} </if> </select>
測試代碼@Test//帶多條件的查詢2:多個條件 public void queryUser(){ SqlSession s=SqlSessionUtil.getSqlSession(); User user=new User(); user.setId("U001"); user.setName("Jack"); List<User> list=s.selectList("queryUser",user); System.out.println(list); }
5)返回結果封裝成List<Map>的查詢
映射文件測試代碼<select id="queryTOList" resultType="hashmap" parameterType="User"> select * from users </select>
@Test//封裝成List<map> public void queryTOList(){ SqlSession s=SqlSessionUtil.getSqlSession(); List<User> list=s.selectList("queryTOList"); System.out.println(list); }
6)返回結果封裝成List<Map>的查詢 ,條件爲hashmap,like查詢
映射文件測試代碼<!-- 返回結果封裝成List<Map>的查詢 ,條件爲hashmap--> <select id="queryTOList2" resultType="hashmap" parameterType="hashmap"> select * from users where 1=1 <if test="id!=null"> and id=#{id} </if> <if test="name!=null"> and name like CONCAT('%','${name}','%' ) </if> </select>
@Test//封裝成List<map> @Test//查詢的輸入參數爲:HashMap public void queryTOList2(){ SqlSession s=SqlSessionUtil.getSqlSession(); Map<String, Object> map = new HashMap<String, Object>(); // map.put("id", "U003"); map.put("name", "J"); List<User> list=s.selectList("queryTOList2",map); System.out.println(list); }
2.增刪改:(一定要提交哦)
1).增:
映射文件@Test//insert(插入) public void insert(){ SqlSession s=SqlSessionUtil.getSqlSession(); User user=new User(); user.setId("U005"); user.setName("BGW"); user.setPwd("55555"); s.insert("insert",user); s.commit();//必須顯示提交。因爲mybatis默認是帶事務的即不自動提交 s.close(); }
測試代碼
<insert id="insert" parameterType="User"> insert into users(id,name,pwd) values(#{id},#{name},#{pwd}) </insert>
2)單
映射文件測試代碼<delete id="delete" parameterType="hashmap"> delete from users where 1=1 <if test="id!=null"> and id=#{id} </if> <if test="name!=null"> and name=#{name} </if> </delete>
@Test//delete刪除 public void delete(){ SqlSession s=SqlSessionUtil.getSqlSession(); Map<String, Object> map = new HashMap<String, Object>(); map.put("id", "U005"); map.put("name", "Jack"); s.delete("delete", map); s.commit();//必須顯示提交。因爲mybatis默認是帶事務的即不自動提交 s.close(); }
3)更新(修改)
映射文件<update id="update" parameterType="User"> update users set id=#{id} <if test="name!=null"> , name=#{name} </if> <if test="pwd!=null"> ,pwd=#{pwd} </if> where id=#{id} </update>
測試代碼@Test//update更新 public void update(){ SqlSession s=SqlSessionUtil.getSqlSession(); Map<String, Object> map = new HashMap<String, Object>(); User user=new User(); user.setId("U005"); user.setName("大哥王"); s.update("update",user); s.commit();//必須顯示提交。因爲mybatis默認是帶事務的即不自動提交 s.close(); }
4)用mybatis調用底層jdbc實現自動生成主鍵
映射文件測試代碼<!-- 用mybatis調用底層jdbc實現自動生成主鍵 --> <insert id="generateKey2" parameterType="hashmap" keyProperty="id" keyColumn="id" useGeneratedKeys="true" > insert into cpu(name) values(#{name}) </insert>
//mybatis中實現自動生成主鍵 @Test//底層jdbc的方式 public void generateKey1() throws SQLException{ SqlSession s=SqlSessionUtil.getSqlSession(); Connection con=s.getConnection(); PreparedStatement pst=con.prepareStatement("insert into cpu(name) values(?)",Statement.RETURN_GENERATED_KEYS); pst.setString(1, "ibm"); int a=pst.executeUpdate(); ResultSet rs=pst.getGeneratedKeys(); rs.next(); s.commit(); s.close(); System.out.println(rs.getInt(1)); } @Test//mybatis的方式 public void generateKey2() throws SQLException{ SqlSession s=SqlSessionUtil.getSqlSession(); Map<String, Object> map=new HashMap<String, Object>(); map.put("name", "intel"); int n=s.insert("generateKey2",map); System.out.println(n); //n是影響的行數 System.out.println(map.get("id")); s.commit(); s.close(); }
3.複雜操作和標籤的操作(m命名空間爲user,基礎配置文件是原來的)
1)多條件查詢
映射文件2)where標籤的使用<select id="dyncUser1" resultType="User" parameterType="User"> select * from users where 1=1 <if test="id!=null"> and id=#{id} </if> <if test="pwd!=null"> and pwd like #{pwd} </if> <if test="name!=null"> and name like #{name} </if> </select>
映射文件3)trim標籤的使用<select id="dyncUser2" resultType="User" parameterType="User"> select * from users <!-- 用where標籤代替上一版本的: where 1=1 --> <where> <if test="id!=null"> and id=#{id}<!-- 解析時第一個and會去掉 --> </if> <if test="pwd!=null"> and pwd like #{pwd} </if> <if test="name!=null"> and name like #{name} </if> </where> </select>
映射文件4)set標籤的使用<!-- trim的使用 --> <select id="dyncUser3" resultType="User" parameterType="User"> select * from users <!-- 用前綴“where”去覆蓋第一個子句中的“and”或“or” --> <trim prefix="where" prefixOverrides="and|or"> <if test="id!=null"> and id=#{id} </if> <if test="pwd!=null"> and pwd like #{pwd} </if> <if test="name!=null"> and name like #{name} </if> </trim> </select>
映射文件<!-- 使用set標籤進行更新記錄 --> <update id="dyncUpdate" parameterType="User"> update users <set> <if test="name!=null"> name=#{name} </if> <if test="pwd!=null"> pwd=#{pwd} </if> </set> where id=#{id} </update>
5)foreach標籤的使用
映射文件
測試代碼<!-- for each執行的效果: ('U001','U002','U010') 相間查詢 --> <select id="dyncUser4" resultType="User" parameterType="list"> select * from users <if test="list!=null and list.size()>0"> where id in <foreach collection="list" index="idx" item="val" open="(" close=")" separator=","> #{val} </foreach> </if> </select>
package cn.hncu.demo; import java.util.ArrayList; import java.util.List; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import cn.hncu.domain.User; import cn.hncu.utils.SqlSessionUtil; public class Demo2 { @Test public void query(){ SqlSession s = SqlSessionUtil.getSqlSession(); User u = new User(); // u.setId("U001"); u.setName("%Jack%"); // u.setPwd("%1%"); // List<User> list=s.selectList("user.dyncUser1", u); // List<User> list=s.selectList("user.dyncUser2", u); List<User> list=s.selectList("user.dyncUser3", u); System.out.println(list); } @Test public void update(){ SqlSession s = SqlSessionUtil.getSqlSession(); User u = new User(); u.setId("U005"); u.setName("Tom"); // u.setPwd("1565"); s.update("dyncUpdate",u); s.commit(); s.close(); } @Test public void queryForeach(){ SqlSession s = SqlSessionUtil.getSqlSession(); List<Object> list=new ArrayList<Object>(); list.add("U001"); list.add("U005"); list=s.selectList("dyncUser4", list); System.out.println(list); s.close(); } }
5.多表操作(namespace="person")
1)一對多查詢操作
法1映射文件(內連接)法2映射文件(左外連接)<!-- 一對多:法1 --> <!--用resultMap來定義一個:自定義複雜結果類型 。注意,column指的是查詢結果集中的字段別名--> <resultMap type="Person" id="ps"><!-- 這裏的colum都是查詢出來的表的column --> <id property="id" column="pid"/> <result property="name" column="pname"/> <collection property="cars" javaType="cn.hncu.domain.Car"> <id property="id" column="cid"/> <result property="name" column="cname" javaType="string" jdbcType="VARCHAR"/> <result property="price" column="cprice" javaType="double" jdbcType="NUMERIC"/> </collection> </resultMap> <!-- resultType只能用於定義簡單類型。要通過resultMap來定義一個:自定義複雜結果類型 --> <!-- 此處是引用上面定義的複雜結果類型 --> <select id="person1" resultMap="ps"> select p.pid as pid,p.pname as pname,c.id as cid,c.name as cname from persons p inner join cars c on p.pid=c.pid </select>
<!-- 一對多:法2 (效果爲left join),子查詢或嵌套查詢 --> <select id="car1" resultType="cn.hncu.domain.Car" parameterType="string"> select *from cars where pid=#{pid} </select> <resultMap type="Person" id="ps2"> <id column="pid" property="id"/> <result property="name" column="pname"/> <collection property="cars" select="car1" column="pid"></collection> </resultMap> <select id="person2" resultMap="ps2"> select pid,pname from persons <!-- 這裏的pname結果無效 --> </select>
測試文件//內聯: 查詢哪些人有哪些車 @Test public void query(){ SqlSession s = SqlSessionUtil.getSqlSession(); // List<Person> persons = s.selectList("person.person1"); List<Person> persons = s.selectList("person.person2"); System.out.println(persons); s.close(); }
2.一對一的多表查詢
映射文件測試文件<!-- 以下演示一對一關係 --> <!-- 同理,下面字段名都是別名 --> <resultMap type="cn.hncu.domain.Card" id="c1"> <!-- <id property="id" column="id"/> 可以--> <constructor> <idArg column="id" javaType="string"/> </constructor> <result property="gov" column="gov" javaType="string" jdbcType="VARCHAR"/> <association property="person" javaType="Person"> <!-- mybatis建議使用id,這裏只是展示用result也可以,但性能有差別 --> <id property="id" column="pid"/> <result property="name" column="pname"/> <collection property="cars" select="car1" column="pid"> </collection> </association> </resultMap> <select id="card" resultMap="c1"> SELECT c.card_id AS id,c.card_gov AS gov ,p.pid AS pid,p.pname AS pname FROM card c INNER JOIN persons p ON c.pid = p.pid </select>
3.多對多的多表查詢//////// 一對一 //////////// @Test public void queryCard(){ SqlSession s = SqlSessionUtil.getSqlSession(); List<Card> cards = s.selectList("person.card"); System.out.println(cards); s.close(); }
映射文件<mapper namespace="role"> <!-- 多對多演示 --> <resultMap type="User" id="ms" > <id property="id" column="uid"/> <result property="name" column="uname"/> <result property="pwd" column="pwd"/> <collection property="roles" javaType="cn.hncu.domain.Role"> <id property="id" column="uid"/> <result property="name" column="關係"/> </collection> </resultMap> <select id="mutilSelect" resultMap="ms"> SELECT u.id AS uid, u.name AS uname,u.pwd AS pwd,r.id AS rid,r.name AS 關係 FROM users u INNER JOIN roleuser ru ON u.id=ru.userid INNER JOIN roles r ON r.id=ru.roleid </select> </mapper>
測試代碼///////// 多對多 //////////// @Test public void mutilSelect(){ SqlSession s = SqlSessionUtil.getSqlSession(); List<User> user = s.selectList("role.mutilSelect"); System.out.println(user); s.close(); }
5.其他操作
1)sql代碼片段(include標籤的使用)<!-- ////sql片段///// --> <sql id="sql1"> id,name,pwd </sql> <select id="queryBySql" resultType="User"> select <include refid="sql1"/> from users </select>
2)緩存機制:mybatis和hibernate一樣,有兩級緩存,第一級是session級別的緩存,第二級是二級緩存
一級緩存演示:
<select id="one" parameterType="string" resultType="cn.hncu.domain.Role" > select *from roles where id=#{id} </select>
測試代碼:結果:兩個r的hashCode一樣是session級別的緩存@Test //mybatis和hibernate一樣,一級緩存是一定存在的,即同一個session對象同享同一個緩存,因此多次獲取相同的對象(內存地址相同即是同一個對象) public void cacheDemo(){ SqlSession s = SqlSessionUtil.getSqlSession(); Role r1=s.selectOne("one", "R001"); System.out.println(r1.hashCode()); Role r2=s.selectOne("one", "R001"); System.out.println(r2.hashCode()); s.close(); }
二級緩存演示:
映射設置
- 在mybatis-config.xml裏設置
<settings> <setting name="cacheEnabled" value="true"/> </settings>
- 在相應的映射文件設置
<!-- 配置緩存(二級)步驟2 --> <cache></cache> <select id="one" parameterType="string" resultType="cn.hncu.domain.Role" > select *from roles where id=#{id} </select>
- 值對象實現Serializable接口
測試:
@Test //mybatis和hibernate一樣,二級緩存需要配置,即兩個session對象都從同一個二級緩存中把同一個對象clone到本地一級緩存,因此兩個session獲取的相同對象的內存地址仍不同,但可以觀察到訪問數據庫只有一次! public void cacheDemo2(){ SqlSession s1 = SqlSessionUtil.getSqlSession(); Role r1=s1.selectOne("one", "R001"); System.out.println("r1"+r1.hashCode()); s1.close();//兩個session寫在一起同時執行,要先把第一個執行完(close),第二級才能從二級緩存中讀取出來有,我的理解爲有事務隔離 SqlSession s2 = SqlSessionUtil.getSqlSession(); Role r2=s2.selectOne("one", "R001"); System.out.println("r2"+r2.hashCode()); s2.close(); }
由於事務的隔離性質,要把第一個SqlSession提交後纔可以進行第二個輸出