最新不知道腦子怎么想的,突然對mybatis特別感興趣,之前在學校的時候學過兩天,有了一個簡單的認識,工作以后,項目中也有用到,趁着興趣還在,抓緊整理一個文檔,方便學習mybatis,同時,自己也在鞏固一下.
mybatis的簡單介紹:
MyBatis的前身叫iBatis,本是apache的一個開源項目, 2010年這個項目由apache software foundation 遷移到了google code,並且改名為MyBatis。MyBatis是支持普通SQL查詢,存儲過程和高級映射的優秀持久層框架。MyBatis消除了幾乎所有的JDBC代碼和參數的手工設置以及結果集的檢索。MyBatis使用簡單的XML或注解用於配置和原始映射,將接口和Java的POJOs(Plan Old Java Objects,普通的Java對象)映射成數據庫中的記錄。
Mybatis的功能架構分為三層(圖片借用了百度百科):
1) API接口層:提供給外部使用的接口API,開發人員通過這些本地API來操縱數據庫。接口層一接收到調用請求就會調用數據處理層來完成具體的數據處理。
2) 數據處理層:負責具體的SQL查找、SQL解析、SQL執行和執行結果映射處理等。它主要的目的是根據調用的請求完成一次數據庫操作。
3) 基礎支撐層:負責最基礎的功能支撐,包括連接管理、事務管理、配置加載和緩存處理,這些都是共用的東西,將他們抽取出來作為最基礎的組件。為上層的數據處理層提供最基礎的支撐。
如圖所示:
為什么要學習mybatis呢,首先我們來創建一個maven項目
如圖所示:
這時mybatis的maven項目就創建好了.
開始在pom.xml中引入相關的jar包
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.j1.mybatis</groupId> <artifactId>mybatis-demo</artifactId> <version>1.0.0-SNAPSHOT</version> <parent> <groupId>cn.j1.parent</groupId> <artifactId>j1-parent</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <dependencies> <!-- MySql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- 單元測試 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </dependency> <!-- Mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> </dependency> </dependencies> </project>
這時項目會報一個小紅叉,不要怕,如圖所示:
先從JDBC程序開始:代碼如下:
package com.j1.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; public class JdbcTest { public static void main(String[] args) { // 數據庫連接 Connection con = null; // 執行sql ResultSet res = null; // 封裝sql PreparedStatement pre = null; // 加載驅動 try { Class.forName("com.mysql.jdbc.Driver"); // 創建連接 // 創建連接 String url = "jdbc:mysql://127.0.0.1:3306/mybatis"; String username = "root"; String password = "root"; con = DriverManager.getConnection(url, username, password); // 獲取PreparedStatement對象 String sql = "select * from tb_user u where u.user_name=?"; pre = con.prepareStatement(sql); // 封裝查詢的參數 pre.setString(1, "wangwu"); // 執行 res = pre.executeQuery(); // 打印結果集, while (res.next()) { System.out.println("username : " + res.getString("user_name")); System.out.println("name : " + res.getString("name")); System.out.println("age : " + res.getInt("age")); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { // 釋放資源 if (pre != null) { pre.close(); } if (res != null) { res.close(); } if (con != null) { con.close(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
查出的結果是:
username : wangwu
name : 王五
age : 22
這個項目跑下來,我們發現很多問題:
1、 數據庫連接字符串、用戶名、密碼等信息硬編碼到java代碼中,維護不方便,修改內容時需要重新編譯java代碼,解決方案:放置到外部配置文件中;
2、 sql語句硬編碼到java代碼中,問題:
a) 維護不方便
b) 代碼重用性差
解決:假設:能否將sql配置到外部文件?
3、 手動設置參數:
a) 將參數值硬編碼到java代碼中
b) 判斷參數類型,使用極其不便;
解決:能否自動設置參數?
4、 手動映射對象,問題:不方便使用,解決方案:能否自動映射?
5、 頻繁的創建連接、關閉連接造成資源浪費,解決方案:使用連接池解決
這時,我們的主角開始上場了,很輕松的解決上面的問題,先寫一個入門案列:
在開發時我們的開發習慣都是面向接口開發,由此,我們來寫一個接口,
根據用戶名查詢數據,到這,先容我抽根煙,
package com.j1.jdbc.Model; import java.util.Date; public class User { private Long id; private String userName; private String password; private String name; private Integer age; private Boolean sex; private Date birthday; private Date created; private Date updated; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Boolean getSex() { return sex; } public void setSex(Boolean sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public Date getCreated() { return created; } public void setCreated(Date created) { this.created = created; } public Date getUpdated() { return updated; } public void setUpdated(Date updated) { this.updated = updated; } @Override public String toString() { return "User [id=" + id + ", userName=" + userName + ", password=" + password + ", name=" + name + ", age=" + age + ", sex=" + sex + ", birthday=" + birthday + ", created=" + created + ", updated=" + updated + "]"; } } //我的第一個mybatisjava程序 package com.j1.mybatis; import java.io.IOException; import java.io.InputStream; 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.j1.jdbc.Model.User; public class Mybatis { public static void main(String[] args) { //讀取配置文件 try { //配置文件 String resource="mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); // 通過SqlSessionFactoryBuilder構建一個SqlSessionFactory SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(inputStream); //System.out.println(sqlSessionFactory); SqlSession session=sqlSessionFactory.openSession(); //System.out.println(session); User user=session.selectOne("com.j1.user.queryUserByUserName", "zhangsan"); System.out.println(user); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } //UserMapper.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="com.j1.user"> <select id="queryUserByUserName" resultType="com.j1.jdbc.Model.User"> SELECT *, user_name userName FROM tb_user WHERE user_name = #{userName} </select> </mapper>
//加入一些配置文件:
#jdbc.properties jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true jdbc.username=root jdbc.password=root jdbc.test.driver=com.mysql.jdbc.Driver jdbc.test.url=jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true jdbc.test.username=root jdbc.test.password=root //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> <!-- 引入外部的配置文件 --> <properties resource="jdbc.properties"/> <settings> <!-- 開啟駝峰自動映射 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <!-- 設置別名 --> <!-- <typeAlias type="com.j1.Model.User" alias="user"/> --> <package name="com.j1.jdbc.Model"/> </typeAliases> <!-- 指定環境 --> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <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> <!-- <environment id="test"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${jdbc.test.driver}" /> <property name="url" value="${jdbc.test.url}" /> <property name="username" value="${jdbc.test.username}" /> <property name="password" value="${jdbc.test.password}" /> </dataSource> </environment> --> </environments> <mappers> <!-- 指定UserMapper.xml --> <mapper resource="UserMapper.xml" /> <!-- <mapper resource="UserMapper2.xml" /> --> <!-- <mapper class="com.j1.mybatis.Iuser"/> --> <!-- 配置掃描包 --> <!-- <package name="com.j1.mybatis"/>--> </mappers> </configuration>
運行的結果是:
User [id=1, userName=zhangsan, password=123456, name=張三, age=30, sex=true, birthday=Wed Aug 08 00:00:00 CST 1984, created=Fri Sep 19 16:56:04 CST 2014, updated=Sun Sep 21 11:24:59 CST 2014]
到這里,說明第一個mybatis程序就運行好了;
總結一下mybati的運行過程,如圖所示:
如果運行過程有問題,可以參考我的另一篇文章,專門講解mybatis的運行原理的,這里就不多說了;
接着,我們使用mybatis完成一個完整的增刪改查的功能,
package com.j1.dao; import java.util.HashMap; import java.util.List; import org.apache.ibatis.annotations.Param; import com.j1.jdbc.Model.User; public interface IUser { /** * 根據用戶名查詢用戶信息 * @param userName * @return */ public User queryUserByUserName(@Param("userName")String userName); /** * 新增用戶數據 * * @param user */ public void saveUser(User user); /** * 更新用戶數據U * @param user */ public void updateUser(User user); /** * 根據id刪除用戶數據 * @param id */ public void deleteUserById(Long id); /** * 通用查詢實現 * * @param tableName * @return */ public List<HashMap<String, Object>> queryByTabkeName(String tableName); /** * 根據用戶密碼查詢 * * @param userName * @param password * @return */ // public User queryUserByUserNameAndPassword(HashMap<String, Object> map); public User queryUserByUserNameAndPassword(@Param("userName")String userName, @Param("password")String password); } package com.j1.dao; import java.util.HashMap; import java.util.List; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import com.j1.jdbc.Model.User; public class UserDao implements IUser { /** * 因為是先入門,暫時沒有和Spring做集成, * 通過構造方法來獲得SqlSessionFactory, */ SqlSessionFactory sqlSessionFactory=null; public UserDao ( SqlSessionFactory sqlSessionFactory){ this.sqlSessionFactory=sqlSessionFactory; } @Override public User queryUserByUserName(String userName) { SqlSession sqlSession=sqlSessionFactory.openSession(true);//默認是false,在進行insert,update之后需要commit,這里我們直接設為true return sqlSession.selectOne("com.j1.user.queryUserByUserName", userName); } @Override public void saveUser(User user) { SqlSession sqlSession = sqlSessionFactory.openSession(true); sqlSession.insert("com.j1.user.saveUser", user); } @Override public void updateUser(User user) { SqlSession sqlSession = sqlSessionFactory.openSession(true); sqlSession.update("com.j1.user.updateUser", user); } @Override public void deleteUserById(Long id) { SqlSession sqlSession = sqlSessionFactory.openSession(true); sqlSession.delete("com.j1.user.deleteUserById", id); } @Override public List<HashMap<String, Object>> queryByTabkeName(String tableName) { return null; } @Override public User queryUserByUserNameAndPassword(String userName, String password) { // TODO Auto-generated method stub return null; } } <?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.j1.user"> <select id="queryUserByUserName" resultType="com.j1.jdbc.Model.User"> SELECT *, user_name userName FROM tb_user WHERE user_name = #{userName} </select> <insert id="saveUser" parameterType="com.j1.jdbc.Model.User" useGeneratedKeys="true" keyProperty="id"> INSERT INTO tb_user ( id, user_name, password, name, age, sex, birthday, created, updated ) VALUES ( NULL, #{userName}, #{password}, #{name}, #{age}, #{sex}, #{birthday}, NOW(), NOW() ); </insert> <update id="updateUser" parameterType="com.j1.jdbc.Model.User"> UPDATE tb_user SET password = #{password}, name = #{name}, age = #{age}, sex = #{sex}, birthday = #{birthday}, updated = NOW() WHERE id = #{id}; </update> <delete id="deleteUserById" parameterType="java.lang.Long"> DELETE FROM tb_user WHERE id = #{id} </delete> </mapper> package com.j1.test; import static org.junit.Assert.*; import java.io.IOException; import java.io.InputStream; import java.util.Date; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import com.j1.dao.IUser; import com.j1.dao.UserDao; import com.j1.jdbc.Model.User; public class UserDAOTest { private static IUser userDAO = null; @Before public void init() { try { // 構造SqlSessionFactory // 定義配置文件路徑 String resource = "mybatis-config.xml"; // 讀取配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); // 通過SqlSessionFactoryBuilder構建一個SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); this.userDAO = new UserDao(sqlSessionFactory); } catch (IOException e) { e.printStackTrace(); } } /** * 這里我們也可用靜態代碼塊 */ static{ String resource = "mybatis-config.xml"; // 讀取配置文件 InputStream inputStream; try { inputStream = Resources.getResourceAsStream(resource); // 通過SqlSessionFactoryBuilder構建一個SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); userDAO = new UserDao(sqlSessionFactory); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Test public void testQueryUserByUserName() { User user = this.userDAO.queryUserByUserName("zhangsan"); System.out.println(user); } @Test public void testSaveUser() { com.j1.jdbc.Model.User user = new User(); user.setAge(20); user.setBirthday(new Date()); user.setName("test_name_4"); user.setPassword("123456"); user.setSex(true); user.setUserName("test_username_4"); this.userDAO.saveUser(user); System.out.println(user); } @Test public void testUpdateUser() { User user = new User(); user.setAge(20); user.setBirthday(new Date()); user.setName("xiaobizaizi"); user.setPassword("123qwe"); user.setSex(true); user.setId(6L); this.userDAO.updateUser(user); } @Test public void testDeleteUserById() { this.userDAO.deleteUserById(6L); } }
//測試通過了
總結:
1 ,定義IUser接口
2,定義Iuser的實現類
3, 編寫Mapper.xml文件
4,編寫測試用例
現在我再來看這個代碼,發現還是有問題
1.1. 接口實現的問題
1、 每次都要openSession,能否自動open
2、 CRUD的實現非常的相像?
有沒有一種簡便的辦法實現接口的實現類?
這就引出動態代理的方法來解決這些問題,只定義接口,不書寫實現類,(動態代理后面我會寫個博客專門來介紹):
package com.j1.test; import static org.junit.Assert.*; import java.io.IOException; import java.io.InputStream; import java.util.Date; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import com.j1.dao.IUser; import com.j1.dao.UserDao; import com.j1.jdbc.Model.User; public class UserDAOTest { private static IUser userDAO = null; @Before public void init() { try { // 構造SqlSessionFactory // 定義配置文件路徑 String resource = "mybatis-config.xml"; // 讀取配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); // 通過SqlSessionFactoryBuilder構建一個SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // this.userDAO = new UserDao(sqlSessionFactory); this.userDAO = sqlSessionFactory.openSession(true).getMapper(IUser.class); } catch (IOException e) { e.printStackTrace(); } } /** * 這里我們也可用靜態代碼塊 */ static{ String resource = "mybatis-config.xml"; // 讀取配置文件 InputStream inputStream; try { inputStream = Resources.getResourceAsStream(resource); // 通過SqlSessionFactoryBuilder構建一個SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); userDAO = new UserDao(sqlSessionFactory); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Test public void testQueryUserByUserName() { User user = this.userDAO.queryUserByUserName("zhangsan"); System.out.println(user); } @Test public void testSaveUser() { com.j1.jdbc.Model.User user = new User(); user.setAge(20); user.setBirthday(new Date()); user.setName("test_name_4"); user.setPassword("123456"); user.setSex(true); user.setUserName("test_username_4"); this.userDAO.saveUser(user); System.out.println(user); } @Test public void testUpdateUser() { User user = new User(); user.setAge(20); user.setBirthday(new Date()); user.setName("test_name_1"); user.setPassword("123qwe"); user.setSex(true); user.setId(6L); this.userDAO.updateUser(user); } @Test public void testDeleteUserById() { this.userDAO.deleteUserById(6L); } }
1.1. 使用Mybatis提供的動態代理實現類實現CRUD
1、 定義接口
2、 定義Mapper.xml,注意:
a) namespace必須是接口的全路徑,如:cn.j1.mybatis.dao.Iuser
b) Statement的id必須要接口中的方法名一致
獲取動態代理實現類:
總結:
如果手動實現接口實現類,namespace和Statement的id隨便定義;
如果使用動態代理實現類,namespace和Statement必須按照約定來定義;
使用mapper接口不用寫接口實現類即可完成數據庫操作,使用非常簡單,也是官方所推薦的使用方法。
使用mapper接口的必須具備以幾個條件:
1) Mapper的namespace必須和mapper接口的全路徑一致。
2) Mapper接口的方法名必須和sql定義的id一致。
3) Mapper接口中方法的輸入參數類型必須和sql定義的parameterType一致。
4) Mapper接口中方法的輸出參數類型必須和sql定義的resultType一致。
至此,mybatis的第一天內容就講的差不多了,下面講一下mybatis-config.xml的一些細節:
<!-- 引入外部的配置文件 --> <properties resource="jdbc.properties"/> <settings> <!-- 開啟駝峰自動映射 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <!-- 設置別名 --> <!-- <typeAlias type="com.j1.Model.User" alias="user"/> --> <package name="com.j1.jdbc.Model"/> </typeAliases> <!-- 指定環境 --> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <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> <!-- <environment id="test"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${jdbc.test.driver}" /> <property name="url" value="${jdbc.test.url}" /> <property name="username" value="${jdbc.test.username}" /> <property name="password" value="${jdbc.test.password}" /> </dataSource> </environment> <mappers> <!-- <mapper resource="UserMapper.xml" /> --> <!-- <mapper resource="UserMapper2.xml" /> --> <!-- <mapper class="com.j1.mybatis.Iuser"/> --> <!-- 配置掃描包 --> <package name="com.j1.dao"/> </mappers> //注意,mybatis的配置是有順序的,不能亂來
此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中。
簡單的mybatis就算是入門了,有時間吧mybatis的一些稍微高深點的東西整理一下,與Spring進行整合,動態sql的拼接,mybatis的緩存機制,mybatis的高級查詢等等