原文連結:https://www.dubby.cn/detail.html?id=9093
1. 瞭解MyBatis
1.1 MyBatis是什麼?
使用Java運算元據庫的話,JDK給我們提供了一層對各個資料庫的封裝,也就是JDBC,它遮蔽了資料庫之間的差異,使用JDBC可以統一操作。但是他長期以來被人詬病的就是重複程式碼太多。封裝引數需要一個一個set,還需要你把ResultSet一個一個對映成POJO,這裡面充滿了重複程式碼,這裡完全可以由模板程式碼來代替。當然,不僅僅是重複程式碼,還有其他缺點,比如,效能,安全,擴充套件性等等。
封裝JDBC以求達到更簡潔,更優雅,更安全,更高效的方案有很多,MyBatis就是其中之一,這類框架一般被叫做ORM框架(Object Relational Mapping,就是Java中的物件和關係型資料庫裡的資料對映的一個框架)。
MyBatis通過構造一個Mapper來消除JDBC程式碼,卻又可以實現查詢資料庫的功能。Mapper是個interface,我們可以指定這個介面裡每個方法對應的SQL模板,然後由MyBatis來繫結執行時傳入的引數給SQL模板裡的佔位符,構造成一個可執行的SQL,然後執行,獲得結果後再自動封裝成方法返回值的型別的物件。
舉個例子,Mapper
:
Blog selectBlog(long id);
複製程式碼
SQL:
select * from Blog where id = #{id}
複製程式碼
這樣查詢時的引數繫結,和查詢結果的封裝都可以由MyBatis來完成。
1.2 為什麼使用MyBatis?
知道MyBatis是什麼,應該就明白為什麼會出現MyBatis之類的ORM框架了,還有常見的Hibernate。對於ORM框架,或者類似的東西,存在的意義就是上面所說的。那麼,為什麼是MyBatis?而不是其他ORM框架呢?這裡我簡單的說說,為什麼我喜歡MyBatis勝過喜歡Hibernate吧,我更喜歡簡潔。說通俗一點,ORM確實有存在的必要,但是真的有必要封裝的太過深嗎?Hiberante可以達到讓使用者完全不知道自己在使用資料庫的程度,這實現起來肯定比MyBatis更復雜,說明Hibernate的技術含量要更高,可是這也帶來了問題,我們真的需要這樣嗎?
過度的封裝,會帶來更多的成本,學習成本、故障解決時的成本。而相比Hibernate的高度複雜,MyBatis就顯得很簡小精悍了。我想這也是很多人喜歡用guice而不是spring吧。
這裡隨意比較一下兩個框架的文件:
2. 基本概念
這裡介紹一下MyBatis最核心的幾個類,雖然框架幫我們隱藏了很多細節,但是如果對他不夠了解,就不能應用的得心應手。這裡介紹的幾個類都很容易理解,名字起的好也是一種本領啊。
2.1 SqlSessionFactoryBuilder
SqlSessionFactoryBuilder
是根據配置建立SqlSessionFactory
的工廠類,一般來說是通過XML,當然,我們也可以直接用程式碼來構建。這個類一旦建立出SqlSessionFactory
之後,他的意義就結束了,可以被釋放了。一般推薦在方法內定義,用完就釋放。當然,你可以用它建立多個不同的SqlSessionFactory
。
2.2 SqlSessionFactory
SqlSessionFactory
一旦建立就應該一直存在,一般建議維護在應用級別(不是方法級別,也不是類級別,而是整個應用共享)。一般來說,不需要重複建立這個物件,也不要釋放它。可以用單例模式來保證整個應用內的唯一性。
2.3 SqlSession
SqlSession
是執行緒不安全的,所以每個執行緒應該維護一個單獨的SqlSession
物件。千萬不要用物件的靜態變數引用來指向一個SqlSession
,這樣會導致它洩露到其他執行緒中。而且,一定要記得關閉SqlSession
,常見的使用方式如下:
SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}
複製程式碼
2.4 Mapper Instances
Mapper Instances
是我們定義的Mapper介面的例項物件,他包含了SQL繫結關係,需要SqlSession
建立。所以他的生命週期可以和SqlSession
一致,也就是執行緒級別,但是為了簡潔,我還是建議放在方法內部,它不需要關閉,使用方法一般如下:
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
// do work
} finally {
session.close();
}
複製程式碼
3. 基本使用
程式碼可直接去Github上下載:https://github.com/dubby1994/mybatis_study
依賴:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
</dependencies>
複製程式碼
3.1 XML版(經典版本)
MyBatis的經典版本就是用XML來配置,並用XML來實現SQL和Java程式碼的分離。
建立資料庫:
CREATE TABLE `blog` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(1024) DEFAULT NULL,
`content` text,
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
複製程式碼
插入資料:
INSERT INTO `blog` (`id`, `title`, `content`, `create_time`)
VALUES
(1, '標題1', '內容1', '2018-02-05 10:45:26');
複製程式碼
編寫主配置檔案mybatis-config.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>
<typeAliases>
<package name="cn.dubby.study.mybatis.basic.xml.entity"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_study?useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=false&serverTimezone=UTC"/>
<property name="username" value="test"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="cn/dubby/study/mybatis/basic/xml/mapper/BlogMapper.xml"/>
</mappers>
</configuration>
複製程式碼
編寫啟動類Main.java
:
package cn.dubby.study.mybatis.basic.xml;
import cn.dubby.study.mybatis.basic.xml.entity.Blog;
import cn.dubby.study.mybatis.basic.xml.mapper.BlogMapper;
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 java.io.IOException;
import java.io.InputStream;
public class Main {
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
Blog blog1 = session.selectOne("cn.dubby.study.mybatis.basic.xml.mapper.BlogMapper.selectBlog", 1);
System.out.println(blog1);
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog2 = mapper.selectBlog(1);
System.out.println(blog2);
} finally {
session.close();
}
}
}
複製程式碼
編寫POJO Blog.java
:
package cn.dubby.study.mybatis.basic.xml.entity;
import java.util.Date;
public class Blog {
private Long id;
private String title;
private String content;
private Date createTime;
@Override
public String toString() {
return "Blog{" +
"id=" + id +
", title='" + title + '\'' +
", content='" + content + '\'' +
", createTime=" + createTime +
'}';
}
//getter and setter ...
}
複製程式碼
編寫Mapper BlogMapper.java
:
package cn.dubby.study.mybatis.basic.xml.mapper;
import cn.dubby.study.mybatis.basic.xml.entity.Blog;
public interface BlogMapper {
Blog selectBlog(long id);
}
複製程式碼
執行Main.java
結果:
Blog{id=1, title='標題1', content='內容1', createTime=Mon Feb 05 18:45:26 CST 2018}
Blog{id=1, title='標題1', content='內容1', createTime=Mon Feb 05 18:45:26 CST 2018}
複製程式碼
3.2 註解版
可以做到完全沒有XML,總共有三個類,Main.java
,Serial.java
,SerialMapper.java
。
先準備資料庫:
CREATE TABLE `serial` (
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(1024) DEFAULT NULL,
`description` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
複製程式碼
INSERT INTO `serial` (`id`, `name`, `description`)
VALUES
(1, 'MyBatis教程', '這一系列文章主要是介紹MyBatis,包含入門使用,高階特性,以及實現細節。');
複製程式碼
Main.java
:
package cn.dubby.study.mybatis.basic.without.xml;
import cn.dubby.study.mybatis.basic.without.xml.entity.Serial;
import cn.dubby.study.mybatis.basic.without.xml.mapper.SerialMapper;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import javax.sql.DataSource;
public class Main {
public static void main(String[] args) {
DataSource dataSource = new UnpooledDataSource("com.mysql.cj.jdbc.Driver", "jdbc:mysql://localhost:3306/mybatis_study?useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=false&serverTimezone=UTC", "test", "123456");
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(SerialMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
SqlSession session = sqlSessionFactory.openSession();
try {
Serial serial1 = session.selectOne("cn.dubby.study.mybatis.basic.without.xml.mapper.SerialMapper.selectSerial", 1);
System.out.println(serial1);
SerialMapper mapper = session.getMapper(SerialMapper.class);
Serial serial2 = mapper.selectSerial(1);
System.out.println(serial2);
} finally {
session.close();
}
}
}
複製程式碼
Serial.java
:
package cn.dubby.study.mybatis.basic.without.xml.entity;
public class Serial {
private Long id;
private String name;
private String description;
@Override
public String toString() {
return "Serial{" +
"id=" + id +
", name='" + name + '\'' +
", description='" + description + '\'' +
'}';
}
//setter and getter...
}
複製程式碼
SerialMapper.java
:
package cn.dubby.study.mybatis.basic.without.xml.mapper;
import cn.dubby.study.mybatis.basic.without.xml.entity.Serial;
import org.apache.ibatis.annotations.Select;
public interface SerialMapper {
@Select("SELECT * FROM serial WHERE id = #{id}")
Serial selectSerial(long id);
}
複製程式碼
執行Main.java
:
Serial{id=1, name='MyBatis教程', description='這一系列文章主要是介紹MyBatis,包含入門使用,高階特性,以及實現細節。'}
Serial{id=1, name='MyBatis教程', description='這一系列文章主要是介紹MyBatis,包含入門使用,高階特性,以及實現細節。'}
複製程式碼