MyBatis 實踐
標簽: Java與存儲
MyBatis簡介
MyBatis 前身是iBatis ,是一個基于Java的數據持久層/對象關系映射(ORM)框架 . MyBatis是對JDBC的封裝,使開發人員只需關注SQL本身,而不需花費過多的精力去處理如注冊驅動 、設置參數 、創建Connection/Statement 、解析結果集 等JDBC過程性代碼.MyBatis基于XML/注解的方式配置Statement,執行SQL,并將執行結果映射成Java對象, 大大降低了數據庫開發的難度.
MyBatis is a first class persistence framework with support for custom SQL, stored procedures and advanced mappings. MyBatis eliminates almost all of the JDBC code and manual setting of parameters and retrieval of results. MyBatis can use simple XML or Annotations for configuration and map primitives, Map interfaces and Java POJOs (Plain Old Java Objects) to database records. – MyBatis項目地址/在線文檔.
初識MyBatis
使用MyBatis需要在pom.xml中添加如下依賴:
<dependency > <groupId > org.mybatis
</groupId > <artifactId > mybatis
</artifactId > <version > 3.3.0
</version >
</dependency >
<dependency > <groupId > mysql
</groupId > <artifactId > mysql-connector-java
</artifactId > <version > 5.1.36
</version >
</dependency >
Select
配置mybatis/mybatis-configuration.xml 作為MyBatis的全局配置文件,其配置了MyBatis的運行環境信息(如數據源/mapper文件等).
<?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://host:port/db?characterEncoding=utf-8" /> <property name ="username" value ="username" /> <property name ="password" value ="password" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="mybatis/mapper/UserDAO.xml" /> </mappers >
</configuration >
書寫UserDAO(mapper映射) 最為MyBatis最核心的部分,配置了操作數據庫的SQL語句 :
<?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 ="namespace" > <select id ="selectUserById" parameterType ="java.lang.Integer" resultType ="com.fq.domain.User" > SELECT * FROM user WHERE id = #{id};
</select > <select id ="selectUserByName" parameterType ="java.lang.String" resultType ="com.fq.domain.User" > SELECT * FROM user WHERE name LIKE '%${value}%';
</select > </mapper >
屬性描述 namespace 命名空間,用于隔離SQL語句 parameterType 定義SQL輸入映射類型,MyBatis通過OGNL 從輸入對象中獲取參數傳入SQL語句. resultType 定義SQL輸出映射類型,MyBatis將SQL查詢結果的一行記錄 映射為resultType指定的類型.
mapper映射文件名有UserDAO.xml/UserMapper.xml/User.xml等幾種形式, 其一般存放在與mybatis-configuration.xml同級的mapper目錄下,由于其主要作用為定義SQL語句與映射關系, 因此一般統稱為mapper映射文件 .
定義PO類 PO類主要作用為SQL(輸入/輸出)映射,通常與數據庫表對應:
/*** @author jifang* @since 15/12/31 下午2:27.*/
public class User {private Integer id;
private String name;
private String password;
public User () {}
public User (Integer id, String name, String password) {
this .id = id;
this .name = name;
this .password = password;}
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;}
public String
getPassword () {
return password;}
public void setPassword (String password) {
this .password = password;}
@Override public String
toString () {
return "User{" +
"id=" + id +
", name='" + name +
'\'' +
", password='" + password +
'\'' +
'}' ;}
}
UserDAO(Java對象) 獲得SqlSession,執行SQL語句, 得到映射結果:
/*** @author jifang* @since 16/2/24 下午6:15.*/
public class UserDAO {private SqlSessionFactory factory;
@Before public void setUp ()
throws IOException {String resource =
"mybatis/mybatis-configuration.xml" ;factory =
new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(resource));}
@Test public void selectUserById () {
try (SqlSession session = factory.openSession()) {User user = session.selectOne(
"namespace.selectUserById" ,
1 );System.out.println(user);}}
@Test public void selectUserByName () {
try (SqlSession session = factory.openSession()) {List<User> users = session.selectList(
"namespace.selectUserByName" ,
"student" );
for (User user : users) {System.out.println(user);}}}
}
Insert
<insert
id =
"insertUser" parameterType=
"com.fq.domain.User" >INSERT INTO user(
name , password) VALUES(
</insert>
@Test
public void insertUser () {
try (SqlSession session = factory.openSession()) {User user =
new User();user.setName(
"new_name1" );user.setPassword(
"new_password" );session.insert(
"namespace.insertUser" , user);session.commit();}
}
自增主鍵返回
修改mapper文件,添加<selectKey/>,可以將MySQL的自增主鍵(即剛剛插入數據時生成的ID)返回:
<insert id ="insertUser" parameterType ="com.fq.domain.User" > <selectKey keyProperty ="id" order ="AFTER" resultType ="java.lang.Integer" > SELECT LAST_INSERT_ID();
</selectKey > INSERT INTO user(name, password) VALUES(#{name}, #{password});
</insert >
屬性描述 keyProperty 指定存儲到DO中的哪個屬性; order selectKey執行順序(相對于insert語句),AFTER/BEFORE; resultType 主鍵返回類型(DO中對應屬性的類型); LAST_INSERT_ID() MySQL函數,返回auto_increment 自增列新記錄值.
@Test
public void insertUser () {
try (SqlSession session = factory.openSession()) {System.
out .println(session);User user =
new User(
null ,
"new_name" ,
"new_password" );session.insert(
"namespace.insertUser" , user);session.commit();System.
out .println(user.getId());}
}
該功能還可以通過<insert/>的useGeneratedKeys/keyProperty兩個屬性合作完成, 詳見MyBatis文檔.
Update
<update id="updateUserById" parameterType="com.fq.domain.User">UPDATE user SET name = #{
name} , password = #{
password} WHERE id = #{
id} ;
</update>
@Test
public void updateUserById () {
try (SqlSession session = factory.openSession(
true )) {session.update(
"namespace.updateUserById" ,
new User(
1 ,
"feiqing" ,
"ICy5YqxZB1uWSwcVLSNLcA==" ));}
}
Delete
<
delete id=
"deleteUserById" parameterType=
"java.lang.Integer" >DELETE FROM user WHERE id =
</
delete >
@Test
public void deleteUserById () {
try (SqlSession session = factory.openSession(
true )) {session.delete(
"namespace.deleteUserById" ,
51615 );}
}
小結
附: 最好在pom.xml中添加一個日志系統實現(logback/log4j), 這樣會在調試程序時打印日志信息,便于查錯, 以logback為例:
<dependency > <groupId > ch.qos.logback
</groupId > <artifactId > logback-classic
</artifactId > <version > 1.1.2
</version >
</dependency >
<configuration > <property name ="logRoot" value ="/data/logs" /> <property name ="pattern" value ="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{0} - %msg%n" /> <appender name ="STDOUT" class ="ch.qos.logback.core.ConsoleAppender" > <encoder > <pattern > ${pattern}
</pattern > </encoder > </appender > <appender name ="FILE" class ="ch.qos.logback.core.rolling.RollingFileAppender" > <rollingPolicy class ="ch.qos.logback.core.rolling.TimeBasedRollingPolicy" > <fileNamePattern > ${logRoot}/common-server.%d{yyyy-MM-dd}.log
</fileNamePattern > <maxHistory > 7
</maxHistory > </rollingPolicy > <encoder > <pattern > ${pattern}
</pattern > </encoder > </appender > <root level ="DEBUG" > <appender-ref ref ="STDOUT" /> <appender-ref ref ="FILE" /> </root > </configuration >
其他關于MyBatis日志的詳細信息可參考MyBatis文檔日志部分.
DAO開發
使用MyBatis開發DAO有兩個方法,原始DAO開發 與Mapper映射DAO開發 .
原始DAO開發
原始DAO開發需要開發人員編寫DAO接口 與DAO實現 ,如根據ID查詢用戶信息:
<
select id=
"selectUserById" parameterType=
"java.lang.Integer" resultType=
"com.fq.domain.User" >
SELECT *
FROM user
WHERE id =
#{id};
</
select >
/*** @author jifang* @since 16/2/22 上午10:20.*/
public interface UserDAO {User selectUserById(Integer id)
throws Exception;
}
public class UserDAOImpl implements UserDAO {private SqlSessionFactory factory;
public UserDAOImpl (SqlSessionFactory factory) {
this .factory = factory;}
@Override public User
selectUserById (Integer id)
throws Exception {SqlSession session = factory.openSession();User user = session.selectOne(
"namespace.selectUserById" , id);session.close();
return user;}
}
public class MyBatisClient {@Test
public void originalClient () throws Exception {UserDAO dao =
new UserDAOImpl(
new SqlSessionFactoryBuilder().build(ClassLoader.getSystemResourceAsStream(
"mybatis/mybatis-configuration.xml" )));User user = dao.selectUserById(
1 );System.
out .println(user);}
}
原始DAO開發中存在的問題: 1) DAO實現方法體中存在很多過程性代碼. 2) 調用SqlSession的方法(select/insert/update)需要指定Statement的id,存在硬編碼,不利于代碼維護.
Mapper映射開發
mapper映射開發方法只需編寫DAO接口,MyBatis根據接口定義與mapper文件中的SQL語句動態創建接口實現.
<?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.fq.mybatis.UserDAO" > <select id ="selectUserById" parameterType ="java.lang.Integer" resultType ="com.fq.domain.User" > SELECT * FROM user WHERE id = #{id};
</select >
</mapper >
注意: 此時namespace必須與UserDAO接口的全限定名相同.
UserDAO接口與前面相同, 但不再使用UserDAOImpl Client
/*** @author jifang* @since 16/2/22 下午2:57.*/
public class MyBatisClient {private SqlSession session;
private SqlSessionFactory factory;
@Before public void setUp () {factory =
new SqlSessionFactoryBuilder().build(ClassLoader.getSystemResourceAsStream(
"mybatis/mybatis-configuration.xml" ));session = factory.openSession();}
@Test public void mapperClient ()
throws Exception {UserDAO dao = session.getMapper(UserDAO.class);User user = dao.selectUserById(
1 );System.out.println(user);}
@After public void tearDown () {session.close();}
}
mapper映射開發方法需要遵循以下規范: mapper文件中的namespace與DAO接口的全限定名相同; mapper文件中的Statement的id與DAO接口方法名相同; mapper文件中的Statement的parameterType/resultType與DAO方法的入參/回參類型相同.
Mapper映射
mapper映射文件(如UserDAO.xml)主要作用是定義SQL語句(每個SQL是一個Statement),是MyBatis的核心.
MyBatis官方推薦使用mapper映射的方法來開發DAO,因此我們以后就不再過多介紹原始DAO的開發.
輸入映射
多個形參
傳遞簡單類型前面示例已經使用過,在此就不再贅述.當需要傳遞多個形參時,不再需要設置parameterType參數:
<update id="updateUserById">UPDATE user SET name = #{
1 }, password = #{
2 } WHERE id = #{
0 };
</update>
void updateUserById(
Integer id,
String name,
String password) throws Exception;
傳入PO
MyBatis使用OGNL 表達式解析對象屬性值:
<
select id=
"selectUserByNamePassword" parameterType=
"com.fq.domain.User" resultType=
"com.fq.domain.User" >
SELECT *
FROM user
WHERE name =
#{name} AND password = #{password};
</
select >
User selectUserByNamePassword(User user)
throws Exception;
傳入Map
<
select id=
"selectUserByMap" parameterType=
"java.util.Map" resultType=
"com.fq.domain.User" >
SELECT *
FROM user
WHERE name =
#{name} AND password = #{password};
</
select >
#{}花括號內對應Map的key.
User selectUserByMap(
Map <
String , Object>
map ) throws Exception;
輸出映射
輸出簡單類型
<
select id=
"selectUserCount" parameterType=
"java.lang.String" resultType=
"java.lang.Integer" >
SELECT count(*)
FROM user
WHERE name
LIKE
</
select >
Integer selectUserCount(
String name) throws Exception;
返回簡單類型必須保證查詢結果只有一行記錄 ,最終將第一個字段的值轉換為輸出類型.
輸出PO對象/列表
前面已經演示過輸出兩種類型(selectUserById/selectUserByName雖然當時使用的是原始DAO開發方法, 但mapper定義形式大同小異),因此在這兒只做簡單總結: 輸出單個PO對象和輸出PO列表在mapper中定義的resultType是一樣的; 輸出單個PO對象要保證SQL查詢結果為單條數據,其內部使用selectOne方法調用; 輸出PO列表表示查詢結果可能為多條 ,其內部使用selectList方法調用,接口返回值可用List<PO>/Set<PO>承載.
輸出Map
輸出PO對象完全可以改用Map輸出,字段名作key,字段值作value .
<
select id=
"selectUserLikeName" resultType=
"java.util.Map" >
SELECT *
FROM user
WHERE name
LIKE
</
select >
List <
Map <
String , Object>> selectUserLikeName(
String name) throws Exception;
resultMap
resultType可將查詢結果映射為PO,但前提是PO屬性名 與SQL字段名 必須一致,如不一致,則可通過resultMap作對應映射:
<resultMap
id =
"userMap" type=
"com.fq.domain.User" ><
id column=
"user_id" property =
"id" /><
result column=
"user_name" property =
"name" /><
result column=
"user_password" property =
"password" />
</resultMap><select
id =
"selectUserByName" parameterType=
"java.lang.String" resultMap=
"userMap" >SELECT
id user_id,
name user_name,password user_passwordFROM userWHERE
name =
</select>
屬性描述 <id/> 表示查詢結果集的唯一標識; <result/> 表示普通結果,即PO屬性; column 表示SQL查詢出來的字段名, property 表示PO屬性.
總結
以上是生活随笔 為你收集整理的MyBatis 实践 -Mapper与DAO 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。