一、JDBC的問題 為什麼我們要使用mybatis,是因為JDBC存在以下問題 1、 資料庫連接創建、釋放頻繁造成系統資源浪費,從而影響系統性能。如果使用資料庫連接池可解決此問題。 2、 Sql語句在代碼中硬編碼,造成代碼不易維護,實際應用中sql變化的可能較大,sql變動需要改變java代碼。 3 ...
一、JDBC的問題
為什麼我們要使用mybatis,是因為JDBC存在以下問題
1、 資料庫連接創建、釋放頻繁造成系統資源浪費,從而影響系統性能。如果使用資料庫連接池可解決此問題。
2、 Sql語句在代碼中硬編碼,造成代碼不易維護,實際應用中sql變化的可能較大,sql變動需要改變java代碼。
3、 使用preparedStatement向占有位符號傳參數存在硬編碼,因為sql語句的where條件不一定,可能多也可能少,修改sql還要修改代碼,系統不易維護。
4、 對結果集解析存在硬編碼(查詢列名),sql變化導致解析代碼變化,系統不易維護,如果能將資料庫記錄封裝成pojo對象解析比較方便。
二、
MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,並且改名為MyBatis 。2013年11月遷移到Github。
MyBatis是一個優秀的持久層框架,它對jdbc的操作資料庫的過程進行封裝,使開發者只需要關註 SQL 本身,而不需要花費精力去處理例如註冊驅動、創建connection、創建statement、手動設置參數、結果集檢索等jdbc繁雜的過程代碼。
Mybatis通過xml或註解的方式將要執行的各種statement(statement、preparedStatemnt、CallableStatement)配置起來,並通過java對象和statement中的sql進行映射生成最終執行的sql語句,最後由mybatis框架執行sql並將結果映射成java對象並返回。
三、
-
mybatis配置
-
通過mybatis環境等配置信息構造SqlSessionFactory即會話工廠
-
由會話工廠創建sqlSession即會話,操作資料庫需要通過sqlSession進行。
-
mybatis底層自定義了Executor執行器介面操作資料庫,Executor介面有兩個實現,一個是基本執行器、一個是緩存執行器。
-
Mapped Statement也是mybatis一個底層封裝對象,它包裝了mybatis配置信息及sql映射信息等。mapper.xml文件中一個sql對應一個Mapped Statement對象,sql的id即是Mapped statement的id
-
Mapped Statement對sql執行輸入參數進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql前將輸入的java對象映射至sql中,輸入參數映射就是jdbc編程中對preparedStatement設置參數。
Mapped Statement對sql執行輸出結果進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執行sql後將輸出結果映射至java對象中,輸出結果映射過程相當於jdbc編程中對結果的解析處理過程。
四、
增加依賴,POM文件內容:
<?xml version="1.0" encoding="UTF-8"?> <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.yuanqinnan</groupId> <artifactId>mybatis-first</artifactId> <version>1.0-SNAPSHOT</version> <name>mybatis-first</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>6.0.6</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> </dependency> </dependencies> </project>
<?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>
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(32) NOT NULL COMMENT '用戶名稱', `birthday` date DEFAULT NULL COMMENT '生日', `sex` char(1) DEFAULT NULL COMMENT '性別', `address` varchar(256) DEFAULT NULL COMMENT '地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES ('1', '王五', null, '2', null); INSERT INTO `user` VALUES ('10', '張三', '2014-07-10', '1', '北京市'); INSERT INTO `user` VALUES ('16', '張小明', null, '1', '河南鄭州'); INSERT INTO `user` VALUES ('22', '陳小明', null, '1', '河南鄭州'); INSERT INTO `user` VALUES ('24', '張三豐', null, '1', '河南鄭州'); INSERT INTO `user` VALUES ('25', '陳小明', null, '1', '河南鄭州'); INSERT INTO `user` VALUES ('26', '王五', null, null, null);
實體:
public class User implements Serializable { private static final long serialVersionUID = 1L; private Integer id; private String username;// 用戶姓名 private String sex;// 性別 private Date birthday;// 生日 private String address;// 地址 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User [id=" + id + ", username=" + username + ", sex=" + sex + ", birthday=" + birthday + ", address=" + address + "]"; } }
<?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"> <!-- namespace:命名空間,用於隔離sql,還有一個很重要的作用,後面會講 --> <mapper namespace="test"> <select id="queryUserById" parameterType="int" resultType="com.yuanqinnan.model.User"> SELECT * FROM `user`where id=#{id} </select> </mapper>
在SqlMapConfig.xml中增加代碼段
<mappers> <!-- 映射文件方式1,一個一個的配置--> <mapper resource="config/sqlmap/User.xml"/> </mappers>
整體結構如下:
public class CRUDTest { //定義 SqlSession SqlSession session =null; @Before public void init(){ //定義mybatis全局配置文件 String resource = "config/SqlMapConfig.xml"; //載入 mybatis 全局配置文件 InputStream inputStream = CRUDTest.class.getClassLoader() .getResourceAsStream(resource); //構建sqlSession的工廠 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //根據 sqlSessionFactory 產生 session session = sessionFactory.openSession(); } //根據id查詢user表數據 @Test public void testSelectUserById(){ String statement = "queryUserById"; User user = session.selectOne(statement, 1); System.out.println(user); session.close(); } }
測試結果:User [id=1, username=王五, sex=2, birthday=null, address=null]
至此,mybatis的功能已經實現,我們按照此例繼續其他的操作
五、其他操作
<!-- 查詢 user 表的所有數據--> <select id="selectUserAll" resultType="com.yuanqinnan.model.User"> select * from user </select>
測試:
//查詢所有user表所有數據 @Test public void testSelectUserAll(){ String statement = "selectUserAll"; List<User> listUser = session.selectList(statement); for(User user : listUser){ System.out.println(user); } session.close(); }
結果:
<!-- 1、${value}裡面必須要寫value,不然會報錯 2、${}表示拼接 sql 字元串,將接收到的參數不加任何修飾拼接在sql語句中 3、使用${}會造成 sql 註入 --> <select id="selectLikeUserName" resultType="com.yuanqinnan.model.User" parameterType="String"> select * from user where username like '%${value}%' <!-- select * from user where username like #{username} --> </select>
測試:
//模糊查詢:根據 user 表的username欄位(用${}實現) @Test public void testSelectLikeUserName(){ String statement = "selectLikeUserName"; List<User> listUser = session.selectList(statement, "三"); for(User user : listUser){ System.out.println(user); } session.close(); }
結果:
<!--#{}實現--> <select id="selectLikeUserName2" resultType="com.yuanqinnan.model.User" parameterType="String"> select * from user where username like #{username} </select>
測試:
//模糊查詢:根據 user 表的username欄位(用#{}實現) @Test public void testSelectLikeUserName2(){ String statement = "selectLikeUserName2"; List<User> listUser = session.selectList(statement, "%三%"); for(User user : listUser){ System.out.println(user); } session.close(); }
結果與上面相同
<!-- 向 user 表插入一條數據 --> <insert id="insertUser" parameterType="com.yuanqinnan.model.User"> insert into user(id,username,sex,birthday,address) value(#{id},#{username},#{sex},#{birthday},#{address}) </insert>
測試:
//向 user 表中插入一條數據 @Test public void testInsertUser(){ String statement = "insertUser"; User user = new User(); user.setUsername("袁帥"); user.setSex("1"); session.insert(statement, user); //提交插入的數據 session.commit(); session.close(); }
結果:
如果我們想要返回當前新增的ID,則需要先獲取自增ID
<!-- 保存用戶 --> <insert id="saveUser" parameterType="com.yuanqinnan.model.User"> <!-- selectKey 標簽實現主鍵返回 --> <!-- keyColumn:主鍵對應的表中的哪一列 --> <!-- keyProperty:主鍵對應的pojo中的哪一個屬性 --> <!-- order:設置在執行insert語句前執行查詢id的sql,在執行insert語句之後執行查詢id的sql --> <!-- resultType:設置返回的id的類型 --> <selectKey keyColumn="id" keyProperty="id" order="AFTER" resultType="int"> SELECT LAST_INSERT_ID() </selectKey> INSERT INTO `user` (username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address}) </insert>
測試:
@Test public void testInsertUser2(){ String statement = "saveUser"; User user = new User(); user.setUsername("袁大帥"); user.setSex("1"); session.insert(statement, user); System.out.println(user); //提交插入的數據 session.commit(); session.close(); }
結果:User [id=29, username=袁大帥, sex=1, birthday=null, address=null]
<!-- 根據 id 更新 user 表的數據 --> <update id="updateUserById" parameterType="com.yuanqinnan.model.User"> update user set username=#{username} where id=#{id} </update>
測試:
//根據 id 更新 user 表的數據 @Test public void testUpdateUserById(){ String statement = "updateUserById"; //如果設置的 id不存在,那麼資料庫沒有數據更改 User user = new User(); user.setId(29); user.setUsername("袁不帥"); session.update(statement, user); session.commit(); session.close(); }
結果:
5.6 刪除用戶
<!-- 根據 id 刪除 user 表的數據 --> <delete id="deleteUserById" parameterType="int"> delete from user where id=#{id} </delete>
測試:
//根據 id 刪除 user 表的數據
@Test
public void testDeleteUserById(){
String statement = "deleteUserById";
session.delete(statement,29);
session.commit();
session.close();
}
結果:刪除成功
#{}和${}
#{}表示一個占位符號,通過#{}可以實現preparedStatement向占位符中設置值,自動進行java類型和jdbc類型轉換。#{}可以有效防止sql註入。 #{}可以接收簡單類型值或pojo屬性值。 如果parameterType傳輸單個簡單類型值,#{}括弧中可以是value或其它名稱。
表示拼接串,通過{}可以將parameterType 傳入的內容拼接在sql中且不進行jdbc類型轉換, 可以接收簡單類型值或屬性值,如果傳輸單個簡單類型值,{}括弧中只能是value。
parameterType和resultType
parameterType:指定輸入參數類型,mybatis通過ognl從輸入對象中獲取參數值拼接在sql中。
resultType:指定輸出結果類型,mybatis將sql查詢結果的一行記錄數據映射為resultType指定類型的對象。如果有多條數據,則分別進行映射,並把對象放到容器List中
selectOne和selectList
selectOne查詢一條記錄,如果使用selectOne查詢多條記錄則拋出異常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3
selectList可以查詢一條或多條記錄。
Mybatis解決jdbc編程的問題
1、 資料庫連接創建、釋放頻繁造成系統資源浪費從而影響系統性能,如果使用資料庫連接池可解決此問題。
解決:在SqlMapConfig.xml中配置數據連接池,使用連接池管理資料庫鏈接。
2、 Sql語句寫在代碼中造成代碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java代碼。
解決:將Sql語句配置在XXXXmapper.xml文件中與java代碼分離。
3、 向sql語句傳參數麻煩,因為sql語句的where條件不一定,可能多也可能少,占位符需要和參數一一對應。
解決:Mybatis自動將java對象映射至sql語句,通過statement中的parameterType定義輸入參數的類型。
4、 對結果集解析麻煩,sql變化導致解析代碼變化,且解析前需要遍歷,如果能將資料庫記錄封裝成pojo對象解析比較方便。
解決:Mybatis自動將sql執行
mybatis與hibernate不同
Mybatis和hibernate不同,它不完全是一個ORM框架,因為MyBatis需要程式員自己編寫Sql語句。mybatis可以通過XML或註解方式靈活配置要運行的sql語句,並將java對象和sql語句映射生成最終執行的sql,最後將sql執行的結果再映射生成java對象。
Mybatis學習門檻低,簡單易學,程式員直接編寫原生態sql,可嚴格控制sql執行性能,靈活度高,非常適合對關係數據模型要求不高的軟體開發,例如互聯網軟體、企業運營類軟體等,因為這類軟體需求變化頻繁,一但需求變化要求成果輸出迅速。但是靈活的前提是mybatis無法做到資料庫無關性,如果需要實現支持多種資料庫的軟體則需要自定義多套sql映射文件,工作量大。
Hibernate對象/關係映射能力強,資料庫無關性好,對於關係模型要求高的軟體(例如需求固定的定製化軟體)如果用hibernate開發可以節省很多代碼,提高效率。但是Hibernate的學習門檻高,要精通門檻更高,而且怎麼設計O/R映射,在性能和對象模型之間如何權衡,以及怎樣用好Hibernate需要具有很強的經驗和能力才行。
總之,按照用戶的需求在有限的資源環境下只要能做出維護性、擴展性良好的軟體架構都是好架構,所以框架只有適合才是最好。
源碼地址:https://github.com/yuanqinnan/javaLearn