程序員應該將核心關注點放在業務上,而不該該將時間過多的浪費在CRUD中,多數的ORM框架都把增長、修改與刪除作得很是不錯了,而後數據庫中查詢無疑是使用頻次最高、複雜度大、與性能密切相關的操做,咱們但願獲得一種使用方便,查詢靈活的ORM框架,MyBatis能夠知足這些要求,MyBatis是一個支持普通SQL查詢,存儲過程和高級映射的優秀持久層框架,它也是SSM框架集成中的重要組成部分。html
ORM能夠解決數據庫與程序間的異構性,好比在Java中咱們使用String表示字符串,而Oracle中可以使用varchar2,MySQL中可以使用varchar,SQLServer可以使用nvarchar。java
對象關係映射(英語:Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),用於實現面向對象編程語言裏不一樣類型系統的數據之間的轉換。簡單的說,ORM是經過使用描述對象和數據庫之間映射的元數據,將程序中的對象與關係數據庫相互映射。mysql
沒有ORM時咱們是這樣完成對象與關係數據庫之間的映射的:git
//將執行的sql String sql = "SELECT name, id, age, password FROM users"; //建立命令對象 preparedStatement = connection.prepareStatement(sql); //執行並得到結果集 resultSet = preparedStatement.executeQuery(); //遍歷結果集,將數據庫中的數據轉換成Java中的對象 while(resultSet.next()){ String name = resultSet.getString("name"); int id = resultSet.getInt("id"); int age = resultSet.getInt("age"); String password = resultSet.getString("password"); User entity= new User(name,id,age,password); Users.add(entity); }
這種方案存在如下不足: 程序員
持久化層缺少彈性。一旦出現業務需求的變動,就必須修改持久化層的接口github
持久化層同時與域模型與關係數據庫模型綁定,無論域模型仍是關係數據庫模型發生變化,都要修改持久化曾的相關程序代碼,增長了軟件的維護難度。算法
將和數據庫交互(CRUD)的代碼硬編碼到JDBC程序中spring
實現見狀的持久化層須要高超的開發技巧,並且編程量很大sql
對象模型和關係模型的轉換很是麻煩數據庫
ORM(O/R Mapping:對象關係映射):
一種將內存中的對象保存到關係型數據庫中的技術
負責實體域對象的持久化,封裝數據庫訪問細節
ORM提供了實現持久化層的另外一種模式,採用映射元數據(XML)來描述對象-關係的映射細節,使得ORM中間件能在任何一個Java應用的業務邏輯層和數據庫之間充當橋樑。
ORM提供了實現持久化層的另外一種模式,它採用映射元數據來描述對象關係的映射,使得ORM中間件能在任何一個應用的業務邏輯層和數據庫層之間充當橋樑。
Java典型的ORM中有:
hibernate:全自動的框架,強大、複雜、笨重、學習成本較高
Mybatis:半自動的框架(懂數據庫的人 才能操做) 必需要本身寫sql
JPA:JPA全稱Java Persistence API、JPA經過JDK 5.0註解或XML描述對象-關係表的映射關係,是Java自帶的框架
ORM的方法論基於三個核心原則:
· 簡單:以最基本的形式建模數據。
· 傳達性:數據庫結構被任何人都能理解的語言文檔化。
· 精確性:基於數據模型建立正確標準化了的結構。
讓咱們從O/R開始。字母O起源於"對象"(Object),而R則來自於"關係"(Relational)。幾乎全部的程序裏面,都存在對象和關係數據庫。在業務邏輯層和用戶界面層中,咱們是面向對象的。當對象信息發生變化的時候,咱們須要把對象的信息保存在關係數據庫中。
當你開發一個應用程序的時候(不使用O/R Mapping),你可能會寫很多數據訪問層的代碼,用來從數據庫保存,刪除,讀取對象信息,等等。你在DAL中寫了不少的方法來讀取對象數據,改變狀態對象等等任務。而這些代碼寫起來老是重複的。
ORM解決的主要問題是對象關係的映射。域模型和關係模型分別是創建在概念模型的基礎上的。域模型是面向對象的,而關係模型是面向關係的。通常狀況下,一個持久化類和一個表對應,類的每一個實例對應表中的一條記錄,類的每一個屬性對應表的每一個字段。
將關係數據庫中表中的記錄映射成爲對象,以對象的形式展示,程序員能夠把對數據庫的操做轉化爲對對象的操做。
所以ORM的目的是爲了方便開發人員以面向對象的思想來實現對數據庫的操做。
優勢:
1.提升了開發效率。因爲ORM能夠自動對Entity對象與數據庫中的Table進行字段與屬性的映射,因此咱們實際可能已經不須要一個專用的、龐大的數據訪問層。
2.ORM提供了對數據庫的映射,不用sql直接編碼,可以像操做對象同樣從數據庫獲取數據。
缺點:
犧牲程序的執行效率和會固定思惟模式,下降了開發的靈活性。
從系統結構上來看,採用ORM的系統通常都是多層系統,系統的層次多了,效率就會下降。ORM是一種徹底的面向對象的作法,而面向對象的作法也會對性能產生必定的影響。
在咱們開發系統時,通常都有性能問題。性能問題主要產生在算法不正確和與數據庫不正確的使用上。ORM所生成的代碼通常不太可能寫出很高效的算法,在數據庫應用上更有可能會被誤用,主要體如今對持久對象的提取和和數據的加工處理上,若是用上了ORM,程序員頗有可能將所有的數據提取到內存對象中,而後再進行過濾和加工處理,這樣就容易產生性能問題。
在對對象作持久化時,ORM通常會持久化全部的屬性,有時,這是不但願的。
但ORM是一種工具,工具確實能解決一些重複,簡單的勞動。這是不能否認的。但咱們不能期望工具能一勞永逸的解決全部問題,有些問題仍是須要特殊處理的,但須要特殊處理的部分對絕大多數的系統,應該是不多的。
MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,而且更名爲MyBatis 。2013年11月遷移到Github。
iBATIS一詞來源於「internet」和「abatis」的組合,是一個基於Java的持久層框架。iBATIS提供的持久層框架包括SQL Maps和Data Access Objects(DAOs)
MyBatis 是一款優秀的持久層框架,它支持定製化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎全部的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可使用簡單的 XML 或註解來配置和映射原生信息,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄。
簡單易學:自己就很小且簡單。沒有任何第三方依賴,最簡單安裝只要兩個jar文件+配置幾個sql映射文件易於學習,易於使用,經過文檔和源代碼,能夠比較徹底的掌握它的設計思路和實現。
靈活:mybatis不會對應用程序或者數據庫的現有設計強加任何影響。 sql寫在xml裏,便於統一管理和優化。經過sql基本上能夠實現咱們不使用數據訪問框架能夠實現的全部功能,或許更多。
解除sql與程序代碼的耦合:經過提供DAO層,將業務邏輯和數據訪問邏輯分離,使系統的設計更清晰,更易維護,更易單元測試。sql和代碼的分離,提升了可維護性。
提供映射標籤,支持對象與數據庫的ORM字段關係映射
提供對象關係映射標籤,支持對象關係組建維護
提供XML標籤,支持編寫動態sql。
(1)、加載配置並初始化
觸發條件:加載配置文件
配置來源於兩個地方,一處是配置文件,一處是Java代碼的註解,將SQL的配置信息加載成爲一個個MappedStatement對象(包括了傳入參數映射配置、執行的SQL語句、結果映射配置),存儲在內存中。
(2)、接收調用請求
觸發條件:調用Mybatis提供的API
傳入參數:爲SQL的ID和傳入參數對象
處理過程:將請求傳遞給下層的請求處理層進行處理。
(3)、處理操做請求 觸發條件:API接口層傳遞請求過來
傳入參數:爲SQL的ID和傳入參數對象
處理過程:
(A)根據SQL的ID查找對應的MappedStatement對象。
(B)根據傳入參數對象解析MappedStatement對象,獲得最終要執行的SQL和執行傳入參數。
(C)獲取數據庫鏈接,根據獲得的最終SQL語句和執行傳入參數到數據庫執行,並獲得執行結果。
(D)根據MappedStatement對象中的結果映射配置對獲得的執行結果進行轉換處理,並獲得最終的處理結果。
(E)釋放鏈接資源。
(4)、返回處理結果將最終的處理結果返回。
不管是用過的hibernate,mybatis,你均可以法相他們有一個共同點:
在java對象和數據庫之間有作mapping的配置文件,也一般是xml 文件
從配置文件(一般是XML配置文件中)獲得 SessionFactory
由SessionFactory 產生 Session
在Session中完成對數據的增刪改查和事務提交等
在用完以後關閉Session
Mybatis的功能架構分爲三層:
API接口層:提供給外部使用的接口API,開發人員經過這些本地API來操縱數據庫。接口層一接收到調用請求就會調用數據處理層來完成具體的數據處理。
數據處理層:負責具體的SQL查找、SQL解析、SQL執行和執行結果映射處理等。它主要的目的是根據調用的請求完成一次數據庫操做。
基礎支撐層:負責最基礎的功能支撐,包括鏈接管理、事務管理、配置加載和緩存處理,這些都是共用的東西,將他們抽取出來做爲最基礎的組件。爲上層的數據處理層提供最基礎的支撐。
主要成員:
Configuration:MyBatis全部的配置信息都保存在Configuration對象之中,配置文件中的大部分配置都會存儲到該類中
SqlSession:做爲MyBatis工做的主要頂層API,表示和數據庫交互時的會話,完成必要數據庫增刪改查功能
Executor:MyBatis執行器,是MyBatis 調度的核心,負責SQL語句的生成和查詢緩存的維護
StatementHandler:封裝了JDBC Statement操做,負責對JDBC statement 的操做,如設置參數等
ParameterHandler:負責對用戶傳遞的參數轉換成JDBC Statement 所對應的數據類型
ResultSetHandler:負責將JDBC返回的ResultSet結果集對象轉換成List類型的集合
TypeHandler:負責java數據類型和jdbc數據類型(也能夠說是數據表列類型)之間的映射和轉換
MappedStatement:MappedStatement維護一條<select|update|delete|insert>節點的封裝
SqlSource:負責根據用戶傳遞的parameterObject,動態地生成SQL語句,將信息封裝到BoundSql對象中,並返回
BoundSql:表示動態生成的SQL語句以及相應的參數信息
層次結構:
更多請參考:《深刻理解mybatis原理》 MyBatis的架構設計以及實例分析
mybatis3中文幫助:http://www.mybatis.org/mybatis-3/zh/index.html
mybatis-spring:http://www.mybatis.org/spring/zh/index.html
MyBatis中國分站:http://www.mybatis.cn/
源代碼:https://github.com/mybatis/mybatis-3/
普通java項目或者是Maven項目均可以,以下圖所示:
下載地址:https://github.com/mybatis/mybatis-3/releases
網盤下載:http://pan.baidu.com/s/1hrB1guo
【MyBatis】
mybatis-3.4.6.jar
【MYSQL驅動包】
mysql-connector-java-5.1.38-bin.jar
Maven 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.zhangguo.mybatis01</groupId> <artifactId>MyBatis01</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--MyBatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!--MySql數據庫驅動 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <!-- JUnit單元測試工具 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> </dependencies> </project>
三、建立數據庫和表,針對MySQL數據庫
SQL腳本以下:
CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) NOT NULL, `sex` enum('boy','girl','secret') DEFAULT 'secret', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
將SQL腳本在MySQL數據庫中執行,完成建立數據庫和表的操做,以下:
表中的數據以下:
在Resources目錄下建立一個conf.xml文件,以下圖所示:
conf.xml文件中的內容以下:
<?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/nfmall"/> <property name="username" value="root"/> <property name="password" value="uchr@123"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/studentMapper.xml"/> </mappers> </configuration>
解釋
Student實體類的代碼以下:
package com.zhangguo.mybatis01.entities; public class Student { private int id; private String name; private String sex; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", sex='" + sex + '\'' + '}'; } }
在resources目錄下建立一個mapper目錄,專門用於存放sql映射文件,在目錄中建立一個studentMapper.xml文件,以下圖所示:
studentMapper.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,namespace的值習慣上設置成包名+sql映射文件名,這樣就可以保證namespace的值是惟一的 例如namespace="com.zhangguo.mybatis01.dao.studentMapper"就是com.zhangguo.mybatis01.dao(包名)+studentMapper(studentMapper.xml文件去除後綴) --> <mapper namespace="com.zhangguo.mybatis01.dao.studentMapper"> <!-- 在select標籤中編寫查詢的SQL語句, 設置select標籤的id屬性爲selectStudentById,id屬性值必須是惟一的,不可以重複 使用parameterType屬性指明查詢時使用的參數類型,resultType屬性指明查詢返回的結果集類型 resultType="com.zhangguo.mybatis01.entities.Student"就表示將查詢結果封裝成一個Student類的對象返回 Student類就是student表所對應的實體類 --> <!-- 根據id查詢獲得一個user對象 --> <select id="selectStudentById" resultType="com.zhangguo.mybatis01.entities.Student"> select * from student where id = #{id} </select> </mapper>
解釋
參考:https://www.cnblogs.com/hellokitty1/p/5216025.html
在配置文件conf.xml中註冊studentMapper.xml映射文件
<?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/nfmall"/> <property name="username" value="root"/> <property name="password" value="uchr@123"/> </dataSource> </environment> </environments> <mappers> <!-- 註冊studentMapper.xml文件studentMapper.xml位於mapper這個目錄下,因此resource寫成mapper/studentMapper.xml--> <mapper resource="mapper/studentMapper.xml"/> </mappers> </configuration>
StudentDao.java,執行定義的select語句
package com.zhangguo.mybatis01.dao; import com.zhangguo.mybatis01.entities.Student; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.InputStream; public class StudentDao { public Student getStudentById(int id){ //使用類加載器加載mybatis的配置文件(它也加載關聯的映射文件) InputStream stream=StudentDao.class.getClassLoader().getResourceAsStream("conf.xml"); //構建sqlSession的工廠 SqlSessionFactory ssf=new SqlSessionFactoryBuilder().build(stream); //使用MyBatis提供的Resources類加載mybatis的配置文件(它也加載關聯的映射文件) //Reader reader = Resources.getResourceAsReader(resource); //構建sqlSession的工廠 //SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader); //建立能執行映射文件中sql的sqlSession SqlSession session=ssf.openSession(); /** * 映射sql的標識字符串, * com.zhangguo.mybatis01.dao.studentMapper是studentMapper.xml文件中mapper標籤的namespace屬性的值, * selectStudentById是select標籤的id屬性值,經過select標籤的id屬性值就能夠找到要執行的SQL */ Student student=session.selectOne("com.zhangguo.mybatis01.dao.studentMapper.selectStudentById",1); return student; } public static void main(String[] args) { StudentDao dao=new StudentDao(); Student student=dao.getStudentById(1); System.out.println(student); } }
執行過程解釋:
一、mybatis配置
SqlMapConfig.xml,此文件做爲mybatis的全局配置文件,配置了mybatis的運行環境等信息。
mapper.xml文件即sql映射文件,文件中配置了操做數據庫的sql語句。此文件須要在SqlMapConfig.xml中加載。
二、經過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編程中對結果的解析處理過程。
參考:https://www.cnblogs.com/selene/p/4604605.html
import com.zhangguo.mybatis01.dao.StudentDao; import com.zhangguo.mybatis01.entities.Student; import org.junit.Test; import org.junit.Before; import org.junit.After; /** * StudentDao Tester. * * @author <Authors name> * @version 1.0 * @since <pre>09/24/2018</pre> */ public class StudentDaoTest { @Before public void before() throws Exception { } @After public void after() throws Exception { } /** * Method: getStudentById(int id) */ @Test public void testGetStudentById() throws Exception { StudentDao dao=new StudentDao(); Student student=dao.getStudentById(1); System.out.println(student); } }
測試結果:
打開IntelliJ IDEA工具,Alt+Ctrl+S,打開設置窗口,點擊進入Plugins。
從插件資源庫中搜索JunitGenerator V2.0版本
安裝此插件,重啓IDEA就能夠了。
如今可經過此工具自動完成test類的生成了,在須要進行單元測試的類中按 Alt+Insert
選中你要建立測試用例的方法便可。
IntelliJ IDEA JUnit Generator自動建立測試用例到指定test目錄
1.打開File->Settings 2.搜索junit,找到JUnit Generator 3.Properties選項卡里的Output Path爲測試用例生成的目錄,修改成test目錄: SOURCEPATH/../../test/java/SOURCEPATH/../../test/java/{PACKAGE}/${FILENAME} 4.切換到JUnit 4選項卡,能夠修改生成測試用例的模板,好比類名、包名等
修改生成位置:
修改模板文件:
測試類生成目錄分析:
${SOURCEPATH}/test/${PACKAGE}/${FILENAME}
$SOURCEPATH/../../test/java/{PACKAGE}/${FILENAME}
對應的目錄結構爲
${SOURCEPATH}是到src/main/java這一層
../是退到上一層目錄的意思,對着圖理解一下
MyBatis可使用XML或註解做爲映射器的描述,XML強大且能夠解偶,註解方便且簡單。
由於每個操做都須要先拿到會話,這裏先定義一個工具類以便複用:
會話工具類:
package com.zhangguo.mybatis02.utils; import com.zhangguo.mybatis02.dao.StudentDao; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; /** * MyBatis 會話工具類 * */ public class SqlSessionFactoryUtil { /** * 得到會話工廠 * * */ public static SqlSessionFactory getFactory(){ InputStream inputStream = null; SqlSessionFactory sqlSessionFactory=null; try{ //加載conf.xml配置文件,轉換成輸入流 inputStream = StudentDao.class.getClassLoader().getResourceAsStream("conf.xml"); //根據配置文件的輸入流構造一個SQL會話工廠 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } finally { if(inputStream!=null){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return sqlSessionFactory; } /** * 得到sql會話,是否自動提交 * */ public static SqlSession openSession(boolean isAutoCommit){ return getFactory().openSession(isAutoCommit); } /** * 關閉會話 * */ public static void closeSession(SqlSession session){ if(session!=null){ session.close(); } } }
XML映射器studentMapper:
<?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.zhangguo.mybatis02.mapper.studentMapper"> <select id="selectStudentById" resultType="com.zhangguo.mybatis02.entities.Student"> SELECT id,name,sex from student where id=#{id} </select> <select id="selectStudentsByName" parameterType="String" resultType="com.zhangguo.mybatis02.entities.Student"> SELECT id,name,sex from student where name like '%${value}%'; </select> <insert id="insertStudent" parameterType="com.zhangguo.mybatis02.entities.Student"> insert into student(name,sex) VALUES(#{name},'${sex}') </insert> <update id="updateStudent" parameterType="com.zhangguo.mybatis02.entities.Student"> update student set name=#{name},sex=#{sex} where id=#{id} </update> <delete id="deleteStudent" parameterType="int"> delete from student where id=#{id} </delete> </mapper>
數據訪問類StudentDao.java:
package com.zhangguo.mybatis02.dao; import com.zhangguo.mybatis02.entities.Student; import com.zhangguo.mybatis02.utils.SqlSessionFactoryUtil; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; public class StudentDao { /** * 根據學生編號得到學生對象 */ public Student selectStudentById(int id) { Student entity = null; //打開一個會話 SqlSession session = SqlSessionFactoryUtil.openSession(true); //查詢單個對象,指定參數爲3 entity = session.selectOne("com.zhangguo.mybatis02.mapper.studentMapper.selectStudentById", id); //關閉 SqlSessionFactoryUtil.closeSession(session); return entity; } /** * 根據學生姓名得到學生集合 */ public List<Student> selectStudentsByName(String name) { List<Student> entities = null; //打開一個會話 SqlSession session = SqlSessionFactoryUtil.openSession(true); //查詢多個對象,指定參數 entities = session.selectList("com.zhangguo.mybatis02.mapper.studentMapper.selectStudentsByName", name); //關閉 SqlSessionFactoryUtil.closeSession(session); return entities; } /** * 添加學生 */ public int insertStudent(Student entity) { //影響行數 int rows=0; //打開一個會話 SqlSession session = SqlSessionFactoryUtil.openSession(true); //執行添加 rows = session.insert("com.zhangguo.mybatis02.mapper.studentMapper.insertStudent", entity); //關閉 SqlSessionFactoryUtil.closeSession(session); return rows; } /** * 更新學生 */ public int updateStudent(Student entity) { //影響行數 int rows=0; //打開一個會話 SqlSession session = SqlSessionFactoryUtil.openSession(true); //執行更新 rows = session.update("com.zhangguo.mybatis02.mapper.studentMapper.updateStudent", entity); //關閉 SqlSessionFactoryUtil.closeSession(session); return rows; } /** * 刪除學生 */ public int deleteStudent(int id) { //影響行數 int rows=0; //打開一個會話 SqlSession session = SqlSessionFactoryUtil.openSession(true); //執行刪除 rows = session.delete("com.zhangguo.mybatis02.mapper.studentMapper.deleteStudent", id); //關閉 SqlSessionFactoryUtil.closeSession(session); return rows; } }
單元測試:
package com.zhangguo.mybatis02.dao; import com.zhangguo.mybatis02.entities.Student; import org.junit.Assert; import org.junit.Test; import org.junit.Before; import org.junit.After; import java.util.List; /** * StudentDao Tester. * * @author <Authors name> * @version 1.0 * @since <pre>09/26/2018</pre> */ public class StudentDaoTest { StudentDao dao; @Before public void before() throws Exception { dao=new StudentDao(); } @After public void after() throws Exception { } /** * Method: selectStudentById(int id) */ @Test public void testSelectStudentById() throws Exception { Student entity=dao.selectStudentById(1); System.out.println(entity); Assert.assertNotNull(entity); } /** * Method: selectStudentsByName(String name) */ @Test public void testSelectStudentsByName() throws Exception { List<Student> students=dao.selectStudentsByName("i"); System.out.println(students); Assert.assertNotNull(students); } /** * Method: insertStudent */ @Test public void testInsertStudent() throws Exception { Student entity=new Student(); entity.setName("瑪麗"); entity.setSex("girl"); Assert.assertEquals(1,dao.insertStudent(entity)); } /** * Method: updateStudent */ @Test public void testUpdateStudent() throws Exception { Student entity=dao.selectStudentById(3); entity.setName("馬力"); entity.setSex("boy"); Assert.assertEquals(1,dao.updateStudent(entity)); } /** * Method: deleteStudent */ @Test public void testDeleteStudent() throws Exception { Assert.assertEquals(1,dao.deleteStudent(2)); } }
測試結果:
參考映射文件1:
<?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:須要和mapper接口的全限定名一致 --> <mapper namespace="com.san.mapper.UserMapper"> <!-- 經過ID查詢用戶 --> <select id="findUserById" parameterType="int" resultType="user"> select * from user where id=#{id} </select> <!-- 定義sql片斷 --> <!-- sql片斷內,能夠定義sql語句中的任何內容 --> <!-- sql片斷內,最好不要使用where和select關鍵字聲明在內 --> <sql id="whereClause"> <!-- if標籤:對輸入的參數進行判斷 --> <!-- test標籤:指定判斷的表達式 --> <if test="user!=null"> <!-- 判斷用戶名不爲空 --> <if test="user.username!=null and user.username!=''"> and username like '%${user.username}%' </if> <!-- 判斷性別不爲空 --> <if test="user.sex!=null and user.sex!=''"> and sex=#{user.sex} </if> </if> <!-- 判斷集合 --> <!-- collection:表示pojo中集合屬性的屬性名稱 --> <!-- item:爲遍歷出的結果聲明一個變量名稱 --> <!-- open:遍歷開始時,須要拼接的字符串 --> <!-- close:遍歷結束時,須要拼接的字符串 --> <!-- separator:遍歷中間須要拼接的字符串 --> <if test="idList!=null"> and id in <foreach collection="idList" item="id" open="(" close=")" separator=","> <!-- and id in (#{id},#{id},#{id}) --> #{id} </foreach> </if> </sql> <!-- 綜合查詢,查詢用戶列表 --> <!-- #{}中的參數名稱要和包裝pojo中的對象層級一致,而且屬性名稱要一致 --> <select id="findUserList" parameterType="com.san.model.UserQueryvo" resultType="user"> select * from user <!-- where標籤:默認去掉後面第一個and,若是沒有參數,則把本身幹掉 --> <where> <!-- 引入sql片斷 --> <include refid="whereClause"></include> </where> </select> <!-- 綜合查詢,查詢用戶的總數 --> <select id="findUserCount" parameterType="com.san.model.UserQueryvo" resultType="int"> select count(*) from user <where> <include refid="whereClause"></include> </where> </select> <!-- id標籤:專門爲查詢結果中惟一列映射 --> <!-- result標籤:映射查詢結果中的普通列 --> <!-- type標籤:返回類型 --> <resultMap type="user" id="UserResMap"> <id column="id_" property="id"/> <result column="username_" property="username"/> <result column="sex_" property="sex"/> </resultMap> <select id="findUserRstMap" parameterType="int" resultMap="UserResMap"> select id id_,username username_,sex sex_ from user where id=#{id} </select> </mapper>
參考映射文件2:
<?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進行分類化的管理,理解爲sql隔離 注意:使用mapper代理開發時,namespace有特殊做用 --> <mapper namespace="test"> <!-- 在映射文件中配置不少sql語句 --> <!-- 需求:經過Id查詢用戶表的記錄 --> <!-- 經過SELECT執行數據庫查詢 id:標識映射文件中的sql,稱爲statement的id; 將sql語句封裝在mapperStatement的對象中,因此Id稱爲Statement的id; parameterType:指定輸入參數的類型,這裏指定int型 #{}:表示一個佔位符; #{id}:其中Id表示接收輸入的參數,參數名稱就是Id,若是輸入參數是簡單類型,#{}中的參數名能夠任意,能夠是value或者其它名稱; resultType:指定sql輸出結果所映射的java對象類型,select指定resultType表示將單條記錄映射成java對象。 --> <select id="findUserById" parameterType="int" resultType="com.mybatis.entity.User" > select * from t_user where id=#{id} </select> <!-- 根據用戶名稱模糊查詢用戶信息,可能返回多條數據 resultType:指定的就是單條記錄所映射的java類型; ${}:表示拼接sql字符串,將接收到的參數內容不加任何修飾拼接在sql中. 使用${}拼接sql,可能會引發sql注入 ${value}:接收輸入參數的內容,若是傳入的是簡單類型,${}中只能使用value --> <select id="findUserByName" parameterType="java.lang.String" resultType="com.mybatis.entity.User" > select * from t_user where username LIKE '%${value}%' </select> <!-- 添加用戶 parameterType:指定輸入的參數類型是pojo(包括用戶信息); #{}中指定pojo的屬性名稱,接收到pojo對象的屬性值 ,mybatis經過OGNL(相似struts2的OGNL)獲取對象的屬性值 --> <insert id="insertUser" parameterType="com.mybatis.entity.User" > <!-- 將insert插入的數據的主鍵返回到User對象中; select last_insert_id():獲得剛insert進去記錄的主鍵值,只適用於自增主鍵; keyProperty:將查詢到的主鍵值,設置到parameterType指定的對象的那個屬性 order:select last_insert_id()執行順序,相對於insert語句來講它的執行順序。 resultType:指定select last_insert_id()的結果類型; --> <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> select last_insert_id() </selectKey> <!-- 使用mysql的uuid(),實現非自增主鍵的返回。 執行過程:經過uuid()獲得主鍵,將主鍵設置到user對象的Id的屬性中,其次,在insert執行時,從user對象中取出Id屬性值; <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String"> select uuid() </selectKey> insert into t_user (id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address}) --> insert into t_user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert> <!-- 刪除用戶 根據ID刪除用戶,須要輸入Id值 --> <delete id="deleteUser" parameterType="java.lang.Integer"> delete from t_user where id=#{id} </delete> <!-- 更新用戶 須要傳入用戶的Id和用戶的更新信息 parameterType:指定User對象,包括Id和用戶的更新信息,注意:Id是必須存在的 #{id}:從輸入的User對象中獲取Id的屬性值 --> <update id="updateUser" parameterType="com.mybatis.entity.User"> update t_user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id} </update> </mapper>
映射器,StudentMapper接口:
package com.zhangguo.mybatis02.dao; import com.zhangguo.mybatis02.entities.Student; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import java.util.List; public interface StudentMapper { /** * 根據學生編號得到學生對象 */ @Select("select id,name,sex from student where id=#{id}") Student selectStudentById(int id); /** * 根據學生姓名得到學生集合 */ @Select("SELECT id,name,sex from student where name like '%${value}%'") List<Student> selectStudentsByName(String name); /** * 添加學生 */ @Insert("insert into student(name,sex) values(#{name},#{sex})") int insertStudent(Student entity); /** * 更新學生 */ @Update("update student set name=#{name},sex=#{sex} where id=#{id}") int updateStudent(Student entity); /** * 刪除學生 */ @Delete("delete from student where id=#{id}") int deleteStudent(int id); }
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> <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://127.0.0.1:3306/nfmall?useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="uchr@123"/> </dataSource> </environment> </environments> <mappers> <!--<mapper resource="mapper/studentMapper.xml"/>--> <mapper class="com.zhangguo.mybatis02.dao.StudentMapper"></mapper> </mappers> </configuration>
StudentDaoAnno.java實現對student的數據訪問:
package com.zhangguo.mybatis02.dao; import com.zhangguo.mybatis02.entities.Student; import com.zhangguo.mybatis02.utils.SqlSessionFactoryUtil; import org.apache.ibatis.session.SqlSession; import java.util.List; public class StudentDaoAnno implements StudentMapper { /** * 根據學生編號得到學生對象 */ public Student selectStudentById(int id) { Student entity = null; //打開一個會話 SqlSession session = SqlSessionFactoryUtil.openSession(true); //得到一個映射器 StudentMapper mapper=session.getMapper(StudentMapper.class); //查詢單個對象,指定參數爲3 entity = mapper.selectStudentById(id); //關閉 SqlSessionFactoryUtil.closeSession(session); return entity; } /** * 根據學生姓名得到學生集合 */ public List<Student> selectStudentsByName(String name) { List<Student> entities = null; //打開一個會話 SqlSession session = SqlSessionFactoryUtil.openSession(true); //得到一個映射器 StudentMapper mapper=session.getMapper(StudentMapper.class); //查詢多個對象,指定參數 entities =mapper.selectStudentsByName(name); //關閉 SqlSessionFactoryUtil.closeSession(session); return entities; } /** * 添加學生 */ public int insertStudent(Student entity) { //影響行數 int rows=0; //打開一個會話 SqlSession session = SqlSessionFactoryUtil.openSession(true); //得到一個映射器 StudentMapper mapper=session.getMapper(StudentMapper.class); //執行添加 rows = mapper.insertStudent(entity); //關閉 SqlSessionFactoryUtil.closeSession(session); return rows; } /** * 更新學生 */ public int updateStudent(Student entity) { //影響行數 int rows=0; //打開一個會話 SqlSession session = SqlSessionFactoryUtil.openSession(true); //得到一個映射器 StudentMapper mapper=session.getMapper(StudentMapper.class); //執行更新 rows =mapper.updateStudent(entity); //關閉 SqlSessionFactoryUtil.closeSession(session); return rows; } /** * 刪除學生 */ public int deleteStudent(int id) { //影響行數 int rows=0; //打開一個會話 SqlSession session = SqlSessionFactoryUtil.openSession(true); //得到一個映射器 StudentMapper mapper=session.getMapper(StudentMapper.class); //執行刪除 rows = mapper.deleteStudent(id); //關閉 SqlSessionFactoryUtil.closeSession(session); return rows; } }
單元測試:
package com.zhangguo.mybatis02.dao; import com.zhangguo.mybatis02.entities.Student; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.util.List; /** * StudentDao Tester. * * @author <Authors name> * @version 1.0 * @since <pre>09/26/2018</pre> */ public class StudentDaoAnnoTest { StudentMapper dao; @Before public void before() throws Exception { dao=new StudentDaoAnno(); } @After public void after() throws Exception { } /** * Method: selectStudentById(int id) */ @Test public void testSelectStudentById() throws Exception { Student entity=dao.selectStudentById(1); System.out.println(entity); Assert.assertNotNull(entity); } /** * Method: selectStudentsByName(String name) */ @Test public void testSelectStudentsByName() throws Exception { List<Student> students=dao.selectStudentsByName("e"); System.out.println(students); Assert.assertNotNull(students); } /** * Method: insertStudent */ @Test public void testInsertStudent() throws Exception { Student entity=new Student(); entity.setName("張小強"); entity.setSex("boy"); Assert.assertEquals(1,dao.insertStudent(entity)); } /** * Method: updateStudent */ @Test public void testUpdateStudent() throws Exception { Student entity=dao.selectStudentById(7); entity.setName("張美麗"); entity.setSex("girl"); Assert.assertEquals(1,dao.updateStudent(entity)); } /** * Method: deleteStudent */ @Test public void testDeleteStudent() throws Exception { Assert.assertEquals(1,dao.deleteStudent(7)); } }
測試結果:
參考映射文件:
package com.winterchen.mapper; import com.winterchen.domain.User; import org.apache.ibatis.annotations.*; import java.util.List; import java.util.Map; /** * User映射類 * Created by Administrator on 2017/11/24. */ @Mapper public interface UserMapper { @Select("SELECT * FROM T_USER WHERE PHONE = #{phone}") User findUserByPhone(@Param("phone") String phone); @Insert("INSERT INTO T_USER(NAME, PASSWORD, PHONE) VALUES(#{name}, #{password}, #{phone})") int insert(@Param("name") String name, @Param("password") String password, @Param("phone") String phone); @Insert("INSERT INTO T_USER(NAME, PASSWORD, PHONE) VALUES(" + "#{name, jdbcType=VARCHAR}, #{password, jdbcType=VARCHAR}, #{phone, jdbcType=VARCHAR})") int insertByMap(Map<String, Object> map); @Insert("INSERT INTO T_USER(NAME, PASSWORD, PHONE) VALUES(#{name}, #{password}, #{phone})") int insertByUser(User user); @Update("UPDATE T_USER SET NAME = #{name}, PASSWORD = #{password} WHERE PHONE = #{phone}") void update(User user); @Delete("DELETE FROM T_USER WHERE ID = #{id}") void delete(Integer id); @Results({ @Result(property = "name", column = "NAME"), @Result(property = "password", column = "PASSWORD"), @Result(property = "phone", column = "PHONE") }) @Select("SELECT NAME, PASSWORD, PHONE FROM T_USER") List<User> findAll(); }
parameterType:在映射文件中經過parameterType指定輸入參數的類型。
resultType:在映射文件中經過resultType指定輸出結果的類型
#{}
#{}表示一個佔位符號,#{}接收輸入參數,類型能夠是簡單類型,pojo、hashmap;
若是接收簡單類型,#{}中能夠寫成value或其它名稱;
#{}接收pojo對象值,經過OGNL讀取對象中的屬性值,經過屬性.屬性.屬性...的方式獲取對象屬性值。使用#{}意味着使用的預編譯的語句,即在使用jdbc時的preparedStatement,sql語句中若是存在參數則會使用?做佔位符,咱們知道這種方式能夠防止sql注入,而且在使用#{}時造成的sql語句,已經帶有引號,例,select? * from table1 where id=#{id}? 在調用這個語句時咱們能夠經過後臺看到打印出的sql爲:select * from table1 where id='2' 加入傳的值爲2.也就是說在組成sql語句的時候把參數默認爲字符串。
${}
表示一個拼接符號,會引用sql注入,因此不建議使用${};
${}接收輸入參數,類型能夠是簡單類型,pojo、hashmap;
若是接收簡單類型,${}中只能寫成value;
${}接收pojo對象值,經過OGNL讀取對象中的屬性值,經過屬性.屬性.屬性...的方式獲取對象屬性值。
使用${}時的sql不會當作字符串處理,是什麼就是什麼,如上邊的語句:select * from table1 where id=${id} 在調用這個語句時控制檯打印的爲:select * from table1 where id=2 ,假設傳的參數值爲2
從上邊的介紹能夠看出這兩種方式的區別,咱們最好是能用#{}則用它,由於它能夠防止sql注入,且是預編譯的,在須要原樣輸出時才使用${},如,
select * from ${tableName} order by ${id} 這裏須要傳入表名和按照哪一個列進行排序 ,加入傳入table一、id 則語句爲:select * from table1 order by id
若是是使用#{} 則變成了select * from 'table1' order by 'id' 咱們知道這樣就不對了。
selectOne表示查詢出一條記錄進行映射。若是使用selectOne能夠實現使用selectList也能夠實現(list中只有一個對象),若是查詢結果爲多條則會報錯。
selectList表示查詢出一個列表(多條記錄)進行映射,能夠是0到n條記錄返回。
如下是MyBatis官網對Mapper Annotations的解釋:
Mapper Annotations
Since the very beginning, MyBatis has been an XML driven framework. The configuration is XML based, and the Mapped Statements are defined in XML. With MyBatis 3, there are new options available. MyBatis 3 builds on top of a comprehensive and powerful Java based Configuration API. This Configuration API is the foundation for the XML based MyBatis configuration, as well as the new Annotation based configuration. Annotations offer a simple way to implement simple mapped statements without introducing a lot of overhead.
NOTE : Java Annotations are unfortunately limited in their expressiveness and flexibility. Despite a lot of time spent in investigation, design and trials, the most powerful MyBatis mappings simply cannot be built with Annotations – without getting ridiculous that is. C# Attributes (for example) do not suffer from these limitations, and thus MyBatis.NET will enjoy a much richer alternative to XML. That said, the Java Annotation based configuration is not without its benefits.
翻譯:
(最初MyBatis是基於XML驅動的框架。MyBatis的配置是基於XML的,語句映射也是用XML定義的。對於MyBatis3,有了新的可選方案。MyBatis3 是創建在全面且強大的Java配置API之上的。 該配置API是MyBatis基於XML配置的基礎,也是基於註解配置的基礎。註解提供了簡單的方式去實現簡單的映射語句,不須要花費大量的開銷。
注意:很不幸的是,java註解在表現和靈活性上存在限制。雖然在調研、設計和測試上花費了不少時間,可是最強大的MyBatis映射功能卻沒法用註解實現。這沒有什麼好笑的。舉例來講,C#的特性就沒有這個限制,因此MyBatis.NET 能擁有一個功能豐富的多的XML替代方案。因此,Java基於註解的配置是依賴於其自身特性的。)
長遠來看建議選擇XML做爲映射器
http://www.mybatis.org/mybatis-3/java-api.html
https://www.bilibili.com/video/av32447485/
https://git.coding.net/zhangguo5/MyBatis02.git
一、請使用MyBatis完成一個用戶管理的數據訪問功能,要求實現根據用戶名查詢用戶對象(id,username,password,name,email,state)功能,表中至少5個字段。
二、請分別使用XML與註解兩種方式實現對象用戶表(Users)的單條記錄查詢、多條記錄查詢、增長、修改與刪除功能,要求單元測試經過。
三、添加用戶成功後返回用戶的編號,而不是影響行數。(選做)
四、實現多個條件組合查詢,相似在電商平臺購物能夠選擇0-n個條件,且能夠自由組合。(選做)
五、實現分頁功能。(選做)