MyBatis學習(1):Mybatis使用詳解和入門案例
前言
MyBatis和Hibernate一樣,是一個優秀的持久層框架。已經說過很多次了,原生的jdbc操作存在大量的重複性程式碼(如註冊驅動,建立連線,建立statement,結果集檢測等)。框架的作用就是把這些繁瑣的程式碼封裝,這樣可以讓程式設計師專注於sql語句本身。
MyBatis通過XML或者註解的方式將要執行的sql語句配置起來,並通過java物件和sql語句對映成最終執行的sql語句。最終由MyBatis框架執行sql,並將結果對映成java物件並返回。
正文
一,MyBatis的執行流程
- mybatis配置檔案,包括Mybatis全域性配置檔案和Mybatis對映檔案,其中全域性配置檔案配置了資料來源、事務等資訊;對映檔案配置了SQL執行相關的
資訊。 - mybatis通過讀取配置檔案資訊(全域性配置檔案和對映檔案),構造出SqlSessionFactory,即會話工廠。
- 通過SqlSessionFactory,可以建立SqlSession即會話。Mybatis是通過SqlSession來操作資料庫的。
- SqlSession本身不能直接操作資料庫,它是通過底層的Executor執行器介面來操作資料庫的。Executor介面有兩個實現類,一個是普通執行器,一個是快取執行器(預設)。
- Executor執行器要處理的SQL資訊是封裝到一個底層物件MappedStatement中。該物件包括:SQL語句、輸入引數對映資訊、輸出結果集對映資訊。其中輸入引數和輸出結果的對映型別包括java的簡單型別、HashMap集合物件、POJO物件型別。
二,MyBatis經典入門案例(原始DAO實現使用者表的增刪改查)
1,環境準備
- Jdk環境:jdk1.7.0_72
- Ide環境:eclipse indigo
- 資料庫環境:MySQL 5.1
- Mybatis:3.2.7
mysql建庫:
CREATE TABLE `user4` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) ,
`password` varchar(20) ,
`age` int(11) ,
PRIMARY KEY (`id`)
)
專案目錄如下:
下面一一講解這些檔案。
2,全域性配置檔案:SqlMapConfig.xml
主要是2個標籤:
environments-用於獲取連線池連線,將來與spring整合時,這個就不要啦,由spring來配置資料庫連線。
mappers-用於引用對映檔案。
<?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>
<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://localhost:3306/user"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="User.xml"/>
</mappers>
</configuration>
3,建立主體類:User.class
package com.jimmy.domain;
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
//get,set方法省略
}
4,建立對映檔案:User.xml
對映檔案就是框架的核心啦,下面這個檔案就配置了java物件與資料庫表之間的對映。
我們看到,其中4個標籤:select,insert ,delete ,update 分別對應著“查,增,刪,改”操作。每個標籤中還有一些屬性,下面來解釋下:
- id:給標籤體內的sql操作起個名字,方便呼叫。
- parameterType:傳入引數的型別。傳入java型別,轉化為sql型別,新增到sql語句上。
- resultType:返回結果型別。sql結果集轉化為java型別並返回。
<?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屬性,不然會報錯,可看做包名-->
<mapper namespace="user">
<select id="findUserById" parameterType="int" resultType="com.jimmy.domain.User">
select * from user4 where id = #{id}
</select>
<select id="findUserAll" resultType="com.jimmy.domain.User">
select * from user4
</select>
<insert id="insertUser" parameterType="com.jimmy.domain.User">
insert into user4(username,password,age) values(#{username},#{password},#{age})
</insert>
<delete id="deleteUserById" parameterType="int">
delete from user4 where id=#{id}
</delete>
<update id="updateUserPassword" parameterType="com.jimmy.domain.User">
update user4 set password=#{password} where id=#{id}
</update>
</mapper>
5,建立dao介面和實現類
package com.jimmy.dao;
import java.util.List;
import com.jimmy.domain.User;
// 介面
public interface UserDao {
public User findUserById(int id) throws Exception ;
public List<User> findAllUsers() throws Exception;
public void insertUser(User user) throws Exception;
public void deleteUserById(int id) throws Exception;
public void updateUserPassword(User user) throws Exception;
}
package com.jimmy.dao.impl;
import java.io.InputStream;
import java.util.List;
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 com.jimmy.dao.UserDao;
import com.jimmy.domain.User;
// 實現類
public class UserDaoImpl implements UserDao {
public User findUserById(int id) throws Exception {
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//---------------
User user = session.selectOne("user.findUserById",id); //引數一:namespace.id
//--------------
session.close();
return user;
}
public List<User> findAllUsers() throws Exception {
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//---------------------
List<User> users = session.selectList("user.findUserAll");
//----------------------
session.close();
return users;
}
public void insertUser(User user) throws Exception {
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//---------------------
session.insert("user.insertUser", user);
session.commit(); //增刪改,一定一定要加上commit操作
//----------------------
session.close();
}
public void deleteUserById(int id) throws Exception {
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//---------------------
session.delete("user.deleteUserById", id);
session.commit(); //增刪改,一定一定要加上commit操作
//----------------------
session.close();
}
public void updateUserPassword(User user) throws Exception {
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//---------------------
session.update("user.updateUserPassword", user);
session.commit(); //增刪改,一定一定要加上commit操作
//----------------------
session.close();
}
}
6,編寫測試類
package com.jimmy.test;
import java.util.List;
import org.junit.Test;
import com.jimmy.dao.UserDao;
import com.jimmy.dao.impl.UserDaoImpl;
import com.jimmy.domain.User;
public class Test3 {
@Test
public void testFindUserById() throws Exception{
UserDao userDao = new UserDaoImpl();
User user = userDao.findUserById(7);
System.out.println(user);
}
@Test
public void testFindAllUser() throws Exception{
UserDao userDao = new UserDaoImpl();
List<User> findAllUsers = userDao.findAllUsers();
for (User user2 : findAllUsers) {
System.out.println(user2);
}
}
@Test
public void testInsertUser() throws Exception{
UserDao userDao = new UserDaoImpl();
User user = new User();
user.setUsername("張三");
user.setPassword("lalal");
user.setAge(12);
userDao.insertUser(user);
}
@Test
public void testDeleteUserById() throws Exception{
UserDao userDao = new UserDaoImpl();
userDao.deleteUserById(3);
}
@Test
public void testUpdateUserPassword() throws Exception{
UserDao userDao = new UserDaoImpl();
User user = new User();
user.setId(9);
user.setPassword("newpassword");
userDao.updateUserPassword(user);
}
}
至此,一個完整的Mybatis入門案例已經初步完成。
回過頭來看DAO的實現類,原始dao開發存在一些問題:
- 存在一定量的模板程式碼。比如:通過SqlSessionFactory建立SqlSession;呼叫SqlSession的方法操作資料庫;關閉Sqlsession。
- 存在一些硬編碼。呼叫SqlSession的方法操作資料庫時,需要指定statement的id,這裡存在了硬編碼。
三,Mapper代理開發模式
Mapper代理的開發方式,程式設計師只需要編寫mapper介面(相當於dao介面)即可,而不同再寫dao實現類啦。Mybatis會自動的為mapper介面生成動態代理實現類。不過要實現mapper代理的開發方式,需要遵循一些開發規範。
- mapper介面的全限定名要和mapper對映檔案的namespace的值相同。
- mapper介面的方法名稱要和mapper對映檔案中的statement的id相同。
- mapper介面的方法引數只能有一個,且型別要和mapper對映檔案中statement的parameterType的值保持一致。
- mapper介面的返回值型別要和mapper對映檔案中statement的resultType值或resultMap中的type值保持一致。
上面4句話的意思是:介面的包名,類名,引數,返回值分別對應著對映檔案的namespace,id,parameterType,resultType。
下面來改造上面的例子。
1,環境準備,同上。
2,全域性配置檔案,同上。記得引用新的對映檔案。
3,建立主題類,同上。
4,建立對映檔案:UserMapper.xml。檔名一般加上“Mapper”。
下面這個檔案跟原始dao程式設計只有一點不同,就是namespace值。
<?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屬性,不然會報錯,且mapper開發時設定為Mapper介面的全限定名-->
<mapper namespace="com.jimmy.dao.UserMapper">
<select id="findUserById" parameterType="int" resultType="com.jimmy.domain.User">
select * from user4 where id = #{id}
</select>
<select id="findUserAll" resultType="com.jimmy.domain.User">
select * from user4
</select>
<insert id="insertUser" parameterType="com.jimmy.domain.User">
insert into user4(username,password,age) values(#{username},#{password},#{age})
</insert>
<delete id="deleteUserById" parameterType="int">
delete from user4 where id=#{id}
</delete>
<update id="updateUserPassword" parameterType="com.jimmy.domain.User">
update user4 set password=#{password} where id=#{id}
</update>
</mapper>
5,建立Mapper介面,這裡就沒有實現類啦。
注意,函式一定要注意4點規範。
package com.jimmy.dao;
import java.util.List;
import com.jimmy.domain.User;
public interface UserMapper {
public User findUserById(int id);
public List<User> findUserAll();
public void insertUser(User user);
public void deleteUserById(int id);
public void updateUserPassword(User user);
}
6,編寫測試類
package com.jimmy.test;
import java.io.InputStream;
import java.util.List;
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.junit.Test;
import com.jimmy.dao.UserMapper;
import com.jimmy.domain.User;
public class Test1 {
@Test
public void findUserByID() throws Exception{
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//---------------
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.findUserById(2);
System.out.println(user);
//--------------
session.close();
}
@Test
public void findAll() throws Exception{
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//---------------------
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> user = mapper.findUserAll();
for (User user2 : user) {
System.out.println(user2);
}
//----------------------
session.close();
}
@Test
public void insertTest() throws Exception{
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//---------------------
User user = new User();
user.setUsername("lalala");
user.setPassword("asdf");
user.setAge(12);
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.insertUser(user);
session.commit();
//----------------------
session.close();
}
@Test
public void deleteUserById() throws Exception{
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//---------------------
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.deleteUserById(2);
session.commit();
//----------------------
session.close();
}
@Test
public void updateUserPassword() throws Exception{
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//---------------------
User user = new User();
user.setId(4);
user.setPassword("newPassword3");
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.updateUserPassword(user);
session.commit();
//----------------------
session.close();
}
}
我們看到,現在已經不需要再寫dao介面的實現類啦,而是框架通過對映檔案為我們生成代理類。而那些重複性的程式碼已經轉移到測試程式碼中了,我們在整合spring和Mybatis時,這些重複性程式碼都會在配置檔案中配置。