MyBatis入門程式案例
mybatis下載
mybaits的程式碼由github.com管理,地址:https://github.com/mybatis/mybatis-3/releases。 可從該地址下載mybatis最新框架。
下載之後解壓縮:
案例需求
1 根據使用者id查詢y一個使用者資訊
2 根據使用者名稱模糊查詢使用者資訊列表
3 新增使用者資訊
4 修改使用者資訊
5 刪除使用者資訊
MyBatis入門程式配置
1)新建資料庫mybatis
2)新建表user
CREATE TABLE `NewTable` (
`id` int(4) NOT NULL AUTO_INCREMENT ,
`name` varchar(7) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
PRIMARY KEY (`id`)
)ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci;
3)新建專案mabatis_demo01(以web專案為例)
4)匯入jar包
工程所需加入的Jar包有mybatis核心包、依賴包和mysql資料驅動包。
5)在工程下新建一個原始碼包config,並在其中建立一個日誌記錄檔案–log4j.properties
log4j.properties檔案:
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
mybatis預設使用log4j作為輸出日誌資訊。
6)在config原始碼包中新建MyBatis的配置檔案SqlMapConfig.xml
SqlMapConfig.xml是mybatis的核心配置檔案,我們來直接配置資料來源以及事務管理
<?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>
<!-- 和spring整合後environments配置將廢除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事務管理 -->
<transactionManager type="JDBC" />
<!-- 資料庫連線池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
</configuration>
注意:等後面mybatis和Spring兩個框架整合之後,environments的配置將被廢除。
7)建立一個實體類User
在包com.oak.domain下新建實體類User
User類內容為:
public class User {
//user的id
private Integer id;
//user的name
private String name;
public User() {
super();
}
public User(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + "]";
}
}
8)在原始碼包config下新建一個包sqlmap,在其中建立對映檔案user.xml
在該包內新建user.xml,此檔案即sql對映檔案,檔案中配置了操作資料庫的sql語句,此檔案需要在SqlMapConfig.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="test">
</mapper>
namespace:即名稱空間,其用於隔離sql語句(即不同sql對映檔案中的兩個相同id的sql語句如何來區分),這是當前的作用,後面會講另一層非常重要的作用。
9)載入對映檔案。mybatis框架需要載入對映檔案,將user.xml新增在SqlMapConfig.xml中
在SqlMapConfig.xml配置檔案中新增如下配置資訊
<mappers>
<!-- resource是基於classpath(原始碼包)來查詢的 -->
<mapper resource="sqlmap/user.xml"/>
</mappers>
10)根據id查詢使用者資訊
在user.xml對映檔案中新增如下配置:
<!-- 根據id獲取使用者資訊 -->
<select id="findUserById" parameterType="int" resultType="com.oak.domain.User">
select * from `user` where id=#{id};
</select>
-
< select>標籤用於定義查詢語句,相應的還有增刪改
-
parameterType:引數的資料型別,即定義輸入到sql中的對映型別。
-
resultType:結果的資料型別,如果是pojo則應該給出全路徑。
-
#{id}表示使用PreparedStatement設定佔位符號並將輸入變數id傳到sql中。說白點,#{}作用就是佔位符,相當於JDBC中的?。
11)在com.oak.test包中新建MyBatisTest類,在類中新建測試方法:
@Test
public void findUserById() throws IOException {
// 第一步,建立SqlSessionFactoryBuilder物件
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 第二步,載入配置檔案
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 第三步,建立SqlSessionFactory物件
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
// 第四步,建立SqlSession物件
SqlSession sqlSession = sqlSessionFactory.openSession();
// 第五步,使用SqlSession物件執行查詢,得到User物件
// 第一個引數:執行查詢的StatementId
User user = sqlSession.selectOne("findUserById", 1);
// 第六步,列印結果
System.out.println(user);
// 第七步,釋放資源,每一個sqlSession就是一個連線
sqlSession.close();
}
以上步驟過於繁瑣,而一般來講工廠物件一般在實際開發是單例的,並不需要頻繁地建立,所以我們在此給SqlSessionFactory定義為單例:
public class MyBatisTest {
// 工廠物件一般在我們的系統中是單例的
private SqlSessionFactory sqlSessionFactory=null;
/**
* Junit中的基本註解,不是AOP中的那個,
* 表示在任意使用@Test註解標註的public void方法執行之前執行
* @throws IOException
*/
@Before
public void init() throws IOException{
// 第一步,建立SqlSessionFactoryBuilder物件
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 第二步,載入配置檔案
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 第三步,建立SqlSessionFactory物件
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
@Test
public void findUserById() throws IOException {
// 第四步,建立SqlSession物件
SqlSession sqlSession = sqlSessionFactory.openSession();
// 第五步,使用SqlSession物件執行查詢,得到User物件
// 第一個引數:執行查詢的StatementId
User user = sqlSession.selectOne("findUserById", 1);
// 第六步,列印結果
System.out.println(user);
// 第七步,釋放資源,每一個sqlSession就是一個連線
sqlSession.close();
}
}
執行測試:
12)根據使用者名稱稱模糊查詢使用者資訊列表
在user.xml中新增如下配置:
<!-- 根據名稱模糊查詢使用者資訊列表 -->
<select id="findUserByName" parameterType="String" resultType="com.oak.domain.User">
select * from `user` where name like #{name}
</select>
如果查詢結果返回的是List集合,那麼resultType只需要設定為List集合中的一個元素的資料型別即可。
在測試類中測試:
@Test
public void findUserByName(){
//建立SqlSession物件
SqlSession sqlSession=sqlSessionFactory.openSession();
//執行查詢
List<User> list=sqlSession.selectList("findUserByName","%s%");
System.out.println(list);
sqlSession.close();
}
執行測試,檢視結果:
使用${value}字串拼接指令完成此任務
修改xml中的sql為:
<!-- 根據名稱模糊查詢使用者資訊列表 -->
<select id="findUserByName" parameterType="String" resultType="com.oak.domain.User">
select * from `user` where name like '${value}'
</select>
$ {value}表示使用引數將$ {value}替換,做字串的拼接,${}為字串拼接指令。注意:如果是取簡單資料型別的引數,括號中的值必須為value。
直接執行上述測試方法:
很明顯這種方式在實際開發中是不建議使用的,因為無法防止SQL注入,建議使用#{ }。
小結
1)#{}和${}
#{}:表示一個佔位符號,可以很好地去避免sql注入。其原理是將佔位符位置的整個引數和sql語句兩部分提交給資料庫,資料庫去執行sql語句,去表中匹配所有的記錄是否和整個引數是否一致。
#{}要獲取輸入引數的值:
-
如果輸入引數是簡單資料型別,則#{}中可以寫value或其它名稱。
-
如果輸入引數是pojo物件型別,則#{}可通過OGNL方式去獲取,表示式就是屬性.屬性.屬性…方式。
$ {}:表示一個sql拼接符號,其原理是在向資料庫發出sql之前去拼接好sql再提交給數
據庫執行。
${}要獲取輸入引數的值:
-
如果輸入引數是簡單資料型別,則${}中只能寫value。
-
如果輸入引數是pojo物件型別,則${}可通過OGNL方式去獲取,表示式就是屬性.屬性.屬性…方式。
一般情況下建議使用#{},特殊情況下必須要用${},比如:
-
動態拼接sql中動態組成排序欄位,要通過$ {}將排序欄位傳入sql中。
-
動態拼接sql中動態組成表名,要通過$ {}將表名傳入sql中。
2)parameterType和resultType
parameterType:指定輸入引數型別,mybatis通過ognl方式從輸入物件中獲取引數值拼接在sql中。
resultType:指定輸出結果型別,mybatis將sql查詢結果的一行記錄資料對映為resultType指定型別的物件。
3)selectOne()和selectList()方法
selectOne:查詢一條記錄,如果使用selectOne查詢多條記錄則丟擲異常
selectList:可以查詢一條或多條記錄。
13)新增一條使用者資訊
在xml配置檔案中新增配置:
<!-- 新增一條資訊 -->
<insert id="addUser" parameterType="com.oak.domain.User">
insert into `user` values(null,#{name})
</insert>
如果輸入引數型別是po,那麼#{}中的名稱就是po類中的屬性,不能隨便定義。
測試類中測試
@Test
public void addUser(){
//建立SqlSession物件
SqlSession sqlSession=sqlSessionFactory.openSession();
sqlSession.insert("addUser", new User(null, "ww"));
sqlSession.close();
}
檢視控制檯:
檢視資料庫:
雖然發出了sql語句,但是事務並沒將其提交,而是回滾了。故User物件是無法插入到資料庫user表中的。
應該在執行之後進行事務提交。修改測試方法為:
@Test
public void addUser(){
//建立SqlSession物件
SqlSession sqlSession=sqlSessionFactory.openSession();
sqlSession.insert("addUser", new User(null, "ww"));
sqlSession.commit();
sqlSession.close();
}
檢視控制檯:
檢視資料庫:
事務已提交,可發現數據庫user表中插入一條記錄。
14)MySQL自增主鍵返回
想要得到MySQL資料庫給我們生成的主鍵id,即獲取主鍵。
這裡要用到MySQL資料庫中的一個函式:
- LAST_INSERT_ID():返回auto_increment自增列新記錄id值。該函式是在當前事務下取到你最後生成的id值,而我們應知道查詢操作是沒有開啟事務的,增刪改操作是需要開啟事務的。
為了獲取自動主鍵,更改user.xml中的addUser為:
<!-- 新增一條資訊 返回自動增長主鍵 -->
<insert id="addUser" parameterType="com.oak.domain.User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
insert into `user` values(null,#{name})
</insert>
-
keyProperty:返回的主鍵儲存在po中的哪個屬性(即其對應po的主鍵屬性)。獲取主鍵,實際上是將主鍵取出來之後封裝到了po的主鍵屬性當中。
-
resultType:返回的主鍵是什麼型別(即其對應pojo的主鍵的資料型別)。
-
order:selectKey的執行順序,是相對於insert語句來說的,由於mysql的自增原理,執行完insert語句之後才將主鍵生成,所以這裡selectKey的執行順序為AFTER。
在測試類中修改測試方法為:
@Test
public void addUser(){
//建立SqlSession物件
SqlSession sqlSession=sqlSessionFactory.openSession();
//只給name賦值
User user=new User(null, "zl");
sqlSession.insert("addUser", user);
System.out.println("或者自動增長主鍵:"+user.getId());
sqlSession.commit();
sqlSession.close();
}
檢視測試結果:
檢視資料庫:
15)刪除資訊
在user.xml中新增
<!-- 刪除使用者 -->
<delete id="deleteUser" parameterType="int">
delete from `user` where id=#{id}
</delete>
在測試類中測試:
@Test
public void delUser(){
//建立SqlSession物件
SqlSession sqlSession=sqlSessionFactory.openSession();
sqlSession.delete("deleteUser",1);
sqlSession.commit();
sqlSession.close();
}
16)修改資訊
在user.xml中新增修改:
<!-- 修改資訊 -->
<update id="updateUser" parameterType="com.oak.domain.User">
update `user` set name=#{name} where id=#{id}
</update>
測試:
@Test
public void updateUser(){
//建立SqlSession物件
SqlSession sqlSession=sqlSessionFactory.openSession();
sqlSession.update("updateUser",new User(2, "李四"));
sqlSession.commit();
sqlSession.close();
}
MyBatis解決了JDBC程式設計的問題
-
資料庫連線建立、釋放頻繁造成系統資源浪費從而影響系統性能,如果使用資料庫連線池可解決此問題。
解決:在SqlMapConfig.xml中配置資料連線池,使用連線池管理資料庫連線。 -
Sql語句寫在程式碼中造成程式碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java程式碼。
解決:將Sql語句配置在XXXXmapper.xml檔案中與java程式碼分離。 -
向sql語句傳引數麻煩,因為sql語句的where條件不一定,可能多也可能少,佔位符需要和引數一一對應。
解決: MyBatis自動將java物件對映至sql語句,通過statement中的parameterType定義輸入引數的型別。 -
對結果集解析麻煩,sql變化導致解析程式碼變化,且解析前需要遍歷,如果能將資料庫記錄封裝成pojo物件解析比較方便。
解決: MyBatis自動將sql執行結果對映至java物件,通過statement中的resultType定義輸出結果的型別。