- jdbc程序
在學習MyBatis的時候先簡單了解下JDBC編程的方式,我們以一個簡單的查詢為例,使用JDBC編程,如下:
1 Public static void main(String[] args) { 2 Connection connection = null; 3 PreparedStatement preparedStatement = null; 4 ResultSet resultSet = null; 5 6 try { 7 //加載數據庫驅動 8 Class.forName("com.mysql.jdbc.Driver"); 9 10 //通過驅動管理類獲取數據庫鏈接 11 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/shop?characterEncoding=utf-8", "root", "mysql"); 12 //定義sql語句 ?表示占位符 13 String sql = "select * from user where username = ?"; 14 //獲取預處理statement 15 preparedStatement = connection.prepareStatement(sql); 16 //設置參數,第一個參數為sql語句中參數的序號(從1開始),第二個參數為設置的參數值 17 preparedStatement.setString(1, "王五"); 18 //向數據庫發出sql執行查詢,查詢出結果集 19 resultSet = preparedStatement.executeQuery(); 20 //遍歷查詢結果集 21 while(resultSet.next()){ 22 System.out.println(resultSet.getString("id")+" "+resultSet.getString("username")); 23 } 24 } catch (Exception e) { 25 e.printStackTrace(); 26 }finally{ 27 //釋放資源 28 if(resultSet!=null){ 29 try { 30 resultSet.close(); 31 } catch (SQLException e) { 32 // TODO Auto-generated catch block 33 e.printStackTrace(); 34 } 35 } 36 if(preparedStatement!=null){ 37 try { 38 preparedStatement.close(); 39 } catch (SQLException e) { 40 // TODO Auto-generated catch block 41 e.printStackTrace(); 42 } 43 } 44 if(connection!=null){ 45 try { 46 connection.close(); 47 } catch (SQLException e) { 48 // TODO Auto-generated catch block 49 e.printStackTrace(); 50 } 51 } 52 53 } 54 55 }
從上面可以看出JDBC編程一般要如下步驟:
1、 加載數據庫驅動
2、 創建並獲取數據庫連接
3、 創建jdbc statement對象
4、 設置sql語句
5、 設置sql語句中的參數(使用preparedStatement)
6、 通過statement執行sql並獲取結果
7、 對sql執行結果進行解析處理
8、 釋放資源(resultSet、preparedstatement、connection)
可以看出所有的JDBC編程有好多相同的步驟,如:加載驅動、獲取連接等。也有一些不同的地方,如sql語句、參數、查詢結果等。JDBC編程主要有以下問題:
1、 數據庫鏈接創建、釋放頻繁造成系統資源浪費從而影響系統性能,如果使用數據庫鏈接池可解決此問題。
2、 Sql語句在代碼中硬編碼,造成代碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java代碼。
3、 使用preparedStatement向占有位符號傳參數存在硬編碼,因為sql語句的where條件不一定,可能多也可能少,修改sql還要修改代碼,系統不易維護。
4、 對結果集解析存在硬編碼(查詢列名),sql變化導致解析代碼變化,系統不易維護,如果能將數據庫記錄封裝成pojo對象解析比較方便。
MyBatis就是這樣一種框架,把上面的步驟按模板模式進行封裝,將相同的部分實現,將不同的部分在配置文件中配置,同時支持參數和查詢條件的映射、結果集和java對象的映射,從而簡化編程。
- MyBatis
MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,並且改名為MyBatis,實質上Mybatis對ibatis進行一些改進。
MyBatis是一個優秀的持久層框架,它對jdbc的操作數據庫的過程進行封裝,使開發者只需要關注 SQL 本身,而不需要花費精力去處理例如注冊驅動、創建connection、創建statement、手動設置參數、結果集檢索等jdbc繁雜的過程代碼。
Mybatis通過xml或注解的方式將要執行的各種statement(statement、preparedStatemnt、CallableStatement)配置起來,並通過java對象和statement中的sql進行映射生成最終執行的sql語句,最后由mybatis框架執行sql並將結果映射成java對象並返回。
MyBatis架構圖如下:
1、 mybatis配置
SqlMapConfig.xml,此文件作為mybatis的全局配置文件,配置了mybatis的運行環境等信息。
mapper.xml文件即sql映射文件,文件中配置了操作數據庫的sql語句。此文件需要在SqlMapConfig.xml中加載。
2、 通過mybatis環境等配置信息構造SqlSessionFactory即會話工廠
3、 由會話工廠創建sqlSession即會話,操作數據庫需要通過sqlSession進行。
4、 mybatis底層自定義了Executor執行器接口操作數據庫,Executor接口有兩個實現,一個是基本執行器、一個是緩存執行器。
5、 Mapped Statement也是mybatis一個底層封裝對象,它包裝了mybatis配置信息及sql映射信息等。mapper.xml文件中一個sql對應一個Mapped Statement對象,sql的id即是Mapped statement的id。
6、 Mapped Statement對sql執行輸入參數進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql前將輸入的java對象映射至sql中,輸入參數映射就是jdbc編程中對preparedStatement設置參數。
7、 Mapped Statement對sql執行輸出結果進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql后將輸出結果映射至java對象中,輸出結果映射過程相當於jdbc編程中對結果的解析處理過程。
- MyBatis實例
應用MyBatis來實現簡單的增刪改查,下面分別實現:
對象類如下:

1 public class User { 2 private int id; 3 private String username;// 用戶姓名 4 private String sex;// 性別 5 private Date birthday;// 生日 6 private String address;// 地址 7 public int getId() { 8 return id; 9 } 10 public void setId(int id) { 11 this.id = id; 12 } 13 public String getUsername() { 14 return username; 15 } 16 public void setUsername(String username) { 17 this.username = username; 18 } 19 public String getSex() { 20 return sex; 21 } 22 public void setSex(String sex) { 23 this.sex = sex; 24 } 25 public Date getBirthday() { 26 return birthday; 27 } 28 public void setBirthday(Date birthday) { 29 this.birthday = birthday; 30 } 31 public String getAddress() { 32 return address; 33 } 34 public void setAddress(String address) { 35 this.address = address; 36 } 37 @Override 38 public String toString() { 39 // TODO Auto-generated method stub 40 return this.id+"-"+this.username+"-"+this.sex+"-"+this.address+"-"+this.birthday.toString(); 41 } 42 43 }
SqlMapConfig.xml配置如下:

1 <configuration> 2 <!-- 和spring整合后 environments配置將廢除--> 3 <environments default="development"> 4 <environment id="development"> 5 <!-- 使用jdbc事務管理--> 6 <transactionManager type="JDBC" /> 7 <!-- 數據庫連接池--> 8 <dataSource type="POOLED"> 9 <property name="driver" value="com.mysql.jdbc.Driver" /> 10 <property name="url" value="jdbc:mysql://localhost:3306/shop?characterEncoding=utf-8" /> 11 <property name="username" value="root" /> 12 <property name="password" value="" /> 13 </dataSource> 14 </environment> 15 </environments> 16 <mappers> 17 <mapper resource="sqlmap/User.xml"/> 18 </mappers> 19 </configuration>
1、用戶id查詢一個用戶信息
映射文件如下:
1 <mapper namespace="user"> 2 <select id="findUserById" parameterType="int" resultType="com.luchao.mybatis.first.po.User"> 3 select * from user where id = #{id} 4 </select> 5 </mapper>
注意:namespace :命名空間,對sql進行分類化管理,用於隔離sql語句。
id:和namespace 一起標識statement。
parameterType:定義輸入到sql中的映射類型,#{id}表示使用preparedstatement設置占位符號並將輸入變量id傳到sql。
resultType:定義結果映射類型。
代碼:
1 @Before 2 public void createSqlSessionFactory() throws IOException{ 3 //配置文件 4 String resource = "SqlMapConfig.xml"; 5 InputStream inputStream = Resources.getResourceAsStream(resource); 6 //使用SqlSessionFactoryBuilder從xml配置文件中加載sqlSessionFactory 7 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 8 } 9 //根據id查詢用戶 10 @Test 11 public void findUserByIdTest(){ 12 //數據庫會話實例 13 SqlSession session = null; 14 try { 15 // 創建數據庫會話實例sqlSession 16 session = sqlSessionFactory.openSession(); 17 // 查詢單個記錄,根據用戶id查詢用戶信息 18 User user = session.selectOne("user.findUserById", 10); 19 System.out.println(user); 20 } catch (Exception e) { 21 // TODO Auto-generated catch block 22 e.printStackTrace(); 23 } finally{ 24 if(session!=null){ 25 session.close(); 26 } 27 } 28 }
2、根據用戶名模糊查詢用戶信息
映射文件:
1 <select id="findUserByName" parameterType="java.lang.String" resultType="com.luchao.mybatis.first.po.User"> 2 select * from user where username like '%${value}%' 3 </select>
#{}表示一個占位符號,通過#{}可以實現preparedStatement向占位符中設置值,自動進行java類型和jdbc類型轉換,#{}可以有效防止sql注入。 #{}可以接收簡單類型值或pojo屬性值。 如果parameterType傳輸單個簡單類型值,#{}括號中可以是value或其它名稱。
${}表示拼接sql串,通過${}可以將parameterType 傳入的內容拼接在sql中且不進行jdbc類型轉換, ${}可以接收簡單類型值或pojo屬性值,如果parameterType傳輸單個簡單類型值,${}括號中只能是value。
代碼:
1 //根據用戶名模糊查詢用戶 2 @Test 3 public void findUserByNameTest(){ 4 //數據庫會話實例 5 SqlSession session = null; 6 try { 7 // 創建數據庫會話實例sqlSession 8 session = sqlSessionFactory.openSession(); 9 // 查詢多個記錄,根據用戶姓名模糊查詢用戶信息 10 List<User> userList = session.selectList("user.findUserByName", "張"); 11 System.out.println(userList.size()); 12 } catch (Exception e) { 13 // TODO Auto-generated catch block 14 e.printStackTrace(); 15 } finally{ 16 if(session!=null){ 17 session.close(); 18 } 19 } 20 }
selectOne查詢一條記錄,如果使用selectOne查詢多條記錄則拋出異常:
1 org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3 2 at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)
selectList可以查詢一條或多條記錄。
3、添加用戶
映射文件:
1 <insert id="insertUser" parameterType="com.luchao.mybatis.first.po.User"> 2 <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> 3 select LAST_INSERT_ID() 4 </selectKey> 5 insert into user(username,birthday,sex,address) value (#{username},#{birthday},#{sex},#{address}); 6 </insert>
這里要注意,如果mysql是自增主鍵,映射文件如下:
1 insert id="insertUser" parameterType="cn.itcast.mybatis.po.User"> 2 <!-- selectKey將主鍵返回,需要再返回 --> 3 <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> 4 select LAST_INSERT_ID() 5 </selectKey> 6 insert into user(username,birthday,sex,address) 7 values(#{username},#{birthday},#{sex},#{address}); 8 </insert>
添加selectKey實現將主鍵返回
keyProperty:返回的主鍵存儲在pojo中的哪個屬性
order:selectKey的執行順序,是相對與insert語句來說,由於mysql的自增原理執行完insert語句之后才將主鍵生成,所以這里selectKey的執行順序為after
resultType:返回的主鍵是什么類型
LAST_INSERT_ID():是mysql的函數,返回auto_increment自增列新記錄id值。
mysql是UUID實現,映射文件如下:
1 <insert id="insertUser" parameterType="cn.luchao.mybatis.po.User"> 2 <selectKey resultType="java.lang.String" order="BEFORE" 3 keyProperty="id"> 4 select uuid() 5 </selectKey> 6 insert into user(id,username,birthday,sex,address) 7 values(#{id},#{username},#{birthday},#{sex},#{address}) 8 </insert> 9 注意這里使用的order是“BEFORE”
Oracle使用序列實現,映射文件如下:
1 <insert id="insertUser" parameterType="cn.luchao.mybatis.po.User"> 2 <selectKey resultType="java.lang.Integer" order="BEFORE" 3 keyProperty="id"> 4 SELECT 自定義序列.NEXTVAL FROM DUAL 5 </selectKey> 6 insert into user(id,username,birthday,sex,address) 7 values(#{id},#{username},#{birthday},#{sex},#{address}) 8 </insert> 9 注意這里使用的order是“BEFORE”
代碼:
1 //插入用戶 2 @Test 3 public void insertUserTest(){ 4 //數據庫會話實例 5 SqlSession session = null; 6 try { 7 // 創建數據庫會話實例sqlSession 8 session = sqlSessionFactory.openSession(); 9 //添加用戶信息 10 User user = new User(); 11 user.setAddress("上海"); 12 user.setBirthday(new Date()); 13 user.setSex("1"); 14 user.setUsername("王小二"); 15 session.insert("insertUser",user); 16 //提交事務 17 session.commit(); 18 } catch (Exception e) { 19 // TODO Auto-generated catch block 20 e.printStackTrace(); 21 } finally{ 22 if(session!=null){ 23 session.close(); 24 } 25 } 26 }
4、刪除用戶
映射文件:
1 <delete id="deleteUser" parameterType="int"> 2 delete from user where id=#{id} 3 </delete>
代碼:
1 //刪除用戶 2 @Test 3 public void deleteUserTest(){ 4 //數據庫會話實例 5 SqlSession session = null; 6 try { 7 // 創建數據庫會話實例sqlSession 8 session = sqlSessionFactory.openSession(); 9 session.delete("deleteUser",30); 10 //提交事務 11 session.commit(); 12 } catch (Exception e) { 13 // TODO Auto-generated catch block 14 e.printStackTrace(); 15 } finally{ 16 if(session!=null){ 17 session.close(); 18 } 19 } 20 }
5、修改用戶:
映射文件:
1 <update id="updateUser" parameterType="com.luchao.mybatis.first.po.User"> 2 update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} 3 where id=#{id} 4 </update>
代碼:
1 //修改用戶 2 @Test 3 public void updteaUserTest(){ 4 //數據庫會話實例 5 SqlSession session = null; 6 try { 7 // 創建數據庫會話實例sqlSession 8 session = sqlSessionFactory.openSession(); 9 //修改用戶信息 10 User user = new User(); 11 user.setId(28); 12 user.setAddress("上海"); 13 user.setBirthday(new Date()); 14 user.setSex("0"); 15 user.setUsername("王小二1"); 16 session.update("updateUser", user); 17 //提交事務 18 session.commit(); 19 } catch (Exception e) { 20 // TODO Auto-generated catch block 21 e.printStackTrace(); 22 } finally{ 23 if(session!=null){ 24 session.close(); 25 } 26 } 27 }
- MyBatis解決JDBC編程的問題
1、 數據庫鏈接創建、釋放頻繁造成系統資源浪費從而影響系統性能,如果使用數據庫鏈接池可解決此問題。
解決:在SqlMapConfig.xml中配置數據鏈接池,使用連接池管理數據庫鏈接。
2、 Sql語句寫在代碼中造成代碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java代碼。
解決:將Sql語句配置文件中與java代碼分離。
3、 向sql語句傳參數麻煩,因為sql語句的where條件不一定,可能多也可能少,占位符需要和參數一一對應。
解決:Mybatis自動將java對象映射至sql語句,通過statement中的parameterType定義輸入參數的類型。
4、 對結果集解析麻煩,sql變化導致解析代碼變化,且解析前需要遍歷,如果能將數據庫記錄封裝成pojo對象解析比較方便。
解決:Mybatis自動將sql執行結果映射至java對象,通過statement中的resultType定義輸出結果的類型。
- MyBatis與Habernate的比較
Mybatis和hibernate不同,它不完全是一個ORM框架,因為MyBatis需要程序員自己編寫Sql語句,不過mybatis可以通過XML或注解方式靈活配置要運行的sql語句,並將java對象和sql語句映射生成最終執行的sql,最后將sql執行的結果再映射生成java對象。
Mybatis學習門檻低,簡單易學,程序員直接編寫原生態sql,可嚴格控制sql執行性能,靈活度高,非常適合對關系數據模型要求不高的軟件開發,例如互聯網軟件、企業運營類軟件等,因為這類軟件需求變化頻繁,一但需求變化要求成果輸出迅速。但是靈活的前提是mybatis無法做到數據庫無關性,如果需要實現支持多種數據庫的軟件則需要自定義多套sql映射文件,工作量大。專注是sql本身,需要程序員自己編寫sql語句,sql修改、優化比較方便。mybatis是一個不完全 的ORM框架,可以簡單理解為SQL Mapper,雖然程序員自己寫sql,mybatis 也可以實現映射(輸入映射、輸出映射)。應用場景:適用與需求變化較多的項目,比如:互聯網項目。
Hibernate對象/關系映射能力強,數據庫無關性好,對於關系模型要求高的軟件(例如需求固定的定制化軟件)如果用hibernate開發可以節省很多代碼,提高效率。但是Hibernate的學習門檻高,要精通門檻更高,而且怎么設計O/R映射,在性能和對象模型之間如何權衡,以及怎樣用好Hibernate需要具有很強的經驗和能力才行。是一個標准ORM框架(對象關系映射)。入門門檻較高的,不需要程序寫sql,sql語句自動生成了,對sql語句進行優化、修改比較困難的。應用場景:適用與需求變化不多的中小型項目,比如:后台管理系統,erp、orm、oa。