教小師妹快速入門Mybatis,看這篇就夠了
本文主要內(nèi)容:
傳統(tǒng)JDBC
傳統(tǒng)JDBC編碼格式
- public class DataBaseUtil {
- public static final String URL = "jdbc:mysql://localhost:3306/mblog";
- public static final String USER = "root";
- public static final String PASSWORD = "123456";
- public static void main(String[] args) throws Exception {
- Class.forName("com.mysql.jdbc.Driver");
- //2.
- Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
- //3.
- Statement stmt = conn.createStatement();
- //4.
- ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1");
- //如果有數(shù)據(jù),rs.next()返回true
- while(rs.next()){
- System.out.println("name: "+rs.getString("name")+" 年齡:"+rs.getInt("age"));
- }
- }
- }
上面代碼中知識為了展示JDBC整個(gè)過程(異常和資源是簡單粗暴的處理了,我們關(guān)注的點(diǎn)不在這兩個(gè))。
大致可以分為六個(gè)步驟:
- 加載驅(qū)動程序
- 獲得數(shù)據(jù)庫連接
- 創(chuàng)建一個(gè)Statement對象
- 操作數(shù)據(jù)庫,實(shí)現(xiàn)增刪改查
- 獲取結(jié)果集
- 關(guān)閉資源
從使用層面來說,采用原生態(tài)的JDBC在項(xiàng)目中使用起來成本還是很高的。如果我們的項(xiàng)目中的業(yè)務(wù)相對比較復(fù)雜,數(shù)據(jù)庫表也相對較多,各種操作數(shù)據(jù)庫的增刪改查的方法也會隨之多起來,那么這樣的代碼重復(fù)次數(shù)會非常之多。
傳統(tǒng)JDBC的問題
- 創(chuàng)建數(shù)據(jù)庫的連接存在大量的硬編碼,
- 執(zhí)行statement時(shí)存在硬編碼.
- 頻繁的開啟和關(guān)閉數(shù)據(jù)庫連接,會嚴(yán)重影響數(shù)據(jù)庫的性能,浪費(fèi)數(shù)據(jù)庫的資源.
- 存在大量的重復(fù)性編碼
為了解決以上問題,就誕生了各種各樣替換JDBC的產(chǎn)品。即就是ORM框架。
什么是ORM?
全稱為Object Relational Mapping。對象-映射-關(guān)系型數(shù)據(jù)庫。對象關(guān)系映射(,簡稱ORM,或O/RM,或O/R mapping),用于實(shí)現(xiàn)面向?qū)ο缶幊陶Z言里不同類型系統(tǒng)的數(shù)據(jù)之間的轉(zhuǎn)換。簡單的說,ORM是通過使用描述對象和數(shù)據(jù)庫之間映射的元數(shù)據(jù),將程序中的對象與關(guān)系數(shù)據(jù)庫相互映射。
ORM提供了實(shí)現(xiàn)持久化層的另一種模式,它采用映射元數(shù)據(jù)來描述對象關(guān)系的映射,使得ORM中間件能在任何一個(gè)應(yīng)用的業(yè)務(wù)邏輯層和數(shù)據(jù)庫層之間充當(dāng)橋梁。
我們的項(xiàng)目中是這樣的:
比如說:Apache DbUtils、Spring JDBC、 Hibernate、Ibatis(Mybatis的前生)、Spring Data Jpa等等。
目前最為流行的Mybatis和Spring Data Jpa。本系列我們先講解Mybatis,Jpa后面再講。
ORM的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)1.提高了開發(fā)效率。由于ORM可以自動對Entity對象與數(shù)據(jù)庫中的Table進(jìn)行字段與屬性的映射,所以我們實(shí)際可能已經(jīng)不需要一個(gè)專用的、龐大的數(shù)據(jù)訪問層。2.ORM提供了對數(shù)據(jù)庫的映射,不用sql直接編碼,能夠像操作對象一樣從數(shù)據(jù)庫獲取數(shù)據(jù)。
缺點(diǎn)犧牲程序的執(zhí)行效率和會固定思維模式,降低了開發(fā)的靈活性。
從系統(tǒng)結(jié)構(gòu)上來看,采用ORM的系統(tǒng)一般都是多層系統(tǒng),系統(tǒng)的層次多了,效率就會降低。ORM是一種完全的面向?qū)ο蟮淖龇?,而面向?qū)ο蟮淖龇ㄒ矔π阅墚a(chǎn)生一定的影響。在我們開發(fā)系統(tǒng)時(shí),一般都有性能問題。性能問題主要產(chǎn)生在算法不正確和與數(shù)據(jù)庫不正確的使用上。ORM所生成的代碼一般不太可能寫出很高效的算法,在數(shù)據(jù)庫應(yīng)用上更有可能會被誤用,主要體現(xiàn)在對持久對象的提取和和數(shù)據(jù)的加工處理上,如果用上了ORM,程序員很有可能將全部的數(shù)據(jù)提取到內(nèi)存對象中,然后再進(jìn)行過濾和加工處理,這樣就容易產(chǎn)生性能問題。在對對象做持久化時(shí),ORM一般會持久化所有的屬性,有時(shí),這是不希望的。但ORM是一種工具,工具確實(shí)能解決一些重復(fù),簡單的勞動。這是不可否認(rèn)的。但我們不能指望工具能一勞永逸的解決所有問題,有些問題還是需要特殊處理的,但需要特殊處理的部分對絕大多數(shù)的系統(tǒng),應(yīng)該是很少的。
MyBatis 是什么?
如果在面試的時(shí)候被問到,只要你說出下面三種即可。
MyBatis 是一款優(yōu)秀的持久層框架,它支持自定義 SQL、存儲過程以及高級映射。
MyBatis 免除了幾乎所有的 JDBC 代碼以及設(shè)置參數(shù)和獲取結(jié)果集的工作。
MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為數(shù)據(jù)庫中的記錄。
來自官網(wǎng)。
MyBatis的優(yōu)點(diǎn)和缺點(diǎn)
優(yōu)點(diǎn):
(1)基于SQL語句編程,相當(dāng)靈活,不會對應(yīng)用程序或者數(shù)據(jù)庫的現(xiàn)有設(shè)計(jì)造成任何影響,SQL寫在XML里,解除sql與程序代碼的耦合,便于統(tǒng)一管理;提供XML標(biāo)簽,支持編寫動態(tài)SQL語句,并可重用。
(2)與JDBC相比,減少了50%以上的代碼量,消除了JDBC大量冗余的代碼,不需要手動開關(guān)連接;
(3)很好的與各種數(shù)據(jù)庫兼容(因?yàn)镸yBatis使用JDBC來連接數(shù)據(jù)庫,所以只要JDBC支持的數(shù)據(jù)庫MyBatis都支持)。
(4)能夠與Spring很好的集成;
(5)提供映射標(biāo)簽,支持對象與數(shù)據(jù)庫的ORM字段關(guān)系映射;提供對象關(guān)系映射標(biāo)簽,支持對象關(guān)系組件維護(hù)。
缺點(diǎn)
(1)SQL語句的編寫工作量較大,尤其當(dāng)字段多、關(guān)聯(lián)表多時(shí),對開發(fā)人員編寫SQL語句的功底有一定要求。
(2)SQL語句依賴于數(shù)據(jù)庫,導(dǎo)致數(shù)據(jù)庫移植性差,不能隨意更換數(shù)據(jù)庫。
Mybatis環(huán)境搭建及簡單實(shí)例
創(chuàng)建一張數(shù)據(jù)庫表
創(chuàng)建一張m_user表使用MySQL數(shù)據(jù)庫。
- CREATE TABLE `m_user` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `name` varchar(255) DEFAULT NULL,
- `age` int(11) DEFAULT NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
添加依賴
- <dependency>
- <groupId>org.mybatis</groupId>
- <artifactId>mybatis</artifactId>
- <version>3.5.2</version>
- </dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>8.0.16</version>
- <scope>runtime</scope>
- </dependency>
- </dependencies>
項(xiàng)目結(jié)構(gòu)如下:
創(chuàng)建一個(gè)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>
- <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/mblog?useUnicode=true"/>
- <property name="username" value="root"/>
- <property name="password" value="123456"/>
- </dataSource>
- </environment>
- </environments>
- <mappers>
- <mapper resource="mapper/UserMapper.xml"/>
- </mappers>
- </configuration>
實(shí)體類User
- ublic class User {
- private Integer id;
- private String name;
- private Integer age;
- //set get
- @Override
- public String toString() {
- return "User{" +
- "id=" + id +
- ", name='" + name + '\'' +
- ", age=" + age +
- '}';
- }
- }
創(chuàng)建UserMapper.java
- import com.tian.mybatis.entity.User;
- public interface UserMapper {
- User selectUserById(Integer id);
- }
創(chuàng)建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.tian.mybatis.mapper.UserMapper">
- <select id="selectUserById" resultType="com.tian.mybatis.entity.User">
- select * from m_user where id = #{id}
- </select>
- </mapper>
創(chuàng)建一個(gè)測試類
- import com.tian.mybatis.entity.User;
- 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 MybatisApplication {
- public static void main(String[] args) {
- String resource = "mybatis-config.xml";
- InputStream inputStream = null;
- SqlSession sqlSession =null;
- try {
- inputStream = Resources.getResourceAsStream(resource);
- //工廠模式
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
- //sql操作會話
- sqlSession = sqlSessionFactory.openSession();
- //獲取數(shù)據(jù)并解析成User對象
- User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById", 1);
- System.out.println(user);
- } catch (Exception e) {
- e.printStackTrace();
- }finally {
- try {
- inputStream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- sqlSession.close();
- }
- }
- }
輸出結(jié)果:
- User{id=1, name='tian', age=22}
整體步驟:
另外一種啟動方式
- import com.tian.mybatis.entity.User;
- import org.apache.ibatis.builder.xml.XMLConfigBuilder;
- import org.apache.ibatis.io.Resources;
- 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.session.defaults.DefaultSqlSessionFactory;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.Properties;
- public class MybatisApplication {
- public static void main(String[] args) {
- String resource = "mybatis-config.xml";
- InputStream inputStream = null;
- try {
- inputStream = Resources.getResourceAsStream(resource);
- } catch (IOException e) {
- e.printStackTrace();
- }
- //解析xml文件
- XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, null, null);
- //構(gòu)建一個(gè)SqlSessionFactory工廠類
- SqlSessionFactory sqlSessionFactory = build(parser.parse());
- //創(chuàng)建一個(gè)SqlSession
- SqlSession sqlSession = sqlSessionFactory.openSession();
- //獲取數(shù)據(jù)并解析成User對象
- User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById", 1);
- System.out.println(user);
- }
- public static SqlSessionFactory build(Configuration config) {
- return new DefaultSqlSessionFactory(config);
- }
- }
輸出和上面一樣。還可以直接使用Java代碼而不用mybatis-config.xml。
- //創(chuàng)建數(shù)據(jù)源
- DataSource dataSource = getDataSource();
- TransactionFactory transactionFactory = new JdbcTransactionFactory();
- Environment environment = new Environment("development", transactionFactory, dataSource);
- Configuration configuration = new Configuration(environment);
- configuration.addMapper(BlogMapper.class);
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
小總結(jié)
從上面這個(gè)案例中我們大致能猜到,Mybatis中最主要幾個(gè)組件:
SqlSessionFactoryBuilder
SqlSessionFactory
SqlSession
Mapper
SqlSessionFactoryBuilder這個(gè)類可以被初始、使用和丟棄,如果你已經(jīng)創(chuàng)建好了一個(gè) SqlSessionFactory后就不用再保留它。因此 ,SqlSessionFactoryBuilder 的最好作用域是方法體內(nèi),比如說定義一個(gè)方法變量。
你可以重復(fù)使用SqlSessionFactoryBuilder 生成多個(gè) SqlSessionFactory 實(shí)例,但是最好不要強(qiáng)行保留,因?yàn)?XML 的解析資源要用來做其它更重要的事。
SqlSessionFactory一旦創(chuàng)建,SqlSessionFactory就會在整個(gè)應(yīng)用過程中始終存在。所以沒有理由去銷毀和再創(chuàng)建它,一個(gè) 應(yīng)用運(yùn)行中也不建議多次創(chuàng)建 SqlSessionFactory。如果真的那樣做,會顯得很拙劣。
因此 SqlSessionFactory最好的作用域是 Application??梢杂卸喾N方法實(shí)現(xiàn)。最簡單的方法是單例模式或者是靜態(tài)單例模式。然而這既不是廣泛贊成和好用的。反而,使用 Google Guice 或 Spring 來進(jìn)行依賴反射會更好。這些框架允 許你生成管理器來管理 SqlSessionFactory的單例生命周期
SqlSession每個(gè)線程都有自己的 SqlSession 實(shí)例,SqlSession 實(shí)例是不能被共享,也是不是線程安全的。因此最好 使用 Request 作用域或者方法體作用域。
不要使用類的靜態(tài)變量來引用一個(gè) SqlSession 實(shí)例,甚至不要使用類的一個(gè)實(shí)例變更來引用。永遠(yuǎn)不要在一個(gè)被管理域中引用 SqlSession,比如說在 Servlet 中的HttpSession 中。如果你正在使用 WEB 框架,應(yīng)該讓 SqlSession 跟隨 HTTP 請求的相似作用域。
也就是說,在收到一個(gè) HTTP 請求過后,打開 SqlSession,等返回一個(gè)回應(yīng)以后,立馬關(guān)掉這個(gè) SqlSession。關(guān)閉 SqlSession 是非常重要的。你必須要確保 SqlSession 在 finally 方法體中正常關(guān)閉。
- SqlSession session = sqlSessionFactory.openSession();
- try {
- // do work
- } finally {
- session.close();
- }
使用這種模式來貫穿你的所有代碼,以確保所有數(shù)據(jù)庫資源都被完全關(guān)閉。
Mapper
Mapper 是一種你創(chuàng)建的用于綁定映射語句的接口。Mapper 接口的實(shí)例是用 SqlSession 來獲得的。同樣 , 從技術(shù)上來說,最廣泛的 Mapper 實(shí)例作用域像 SqlSession 一樣,使用請求作用域。確切地說,在方法 被調(diào)用的時(shí)候調(diào)用 Mapper 實(shí)例,然后使用后,就自動銷毀掉。不需要使用明確的注銷。當(dāng)一個(gè)請求執(zhí) 行正確無誤的時(shí)候,像 SqlSession 一樣,你可以輕而易舉地操控這一切。保持簡單性,保持 Mapper 在 方法體作用域內(nèi)。
- //獲取數(shù)據(jù)并解析成User對象
- User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById", 1);
- System.out.println(user);
這里映射涉及到四個(gè)主體:
- 實(shí)體類User。
- 接口UaerMapper。
- xml配置文件UserMapper。
- 數(shù)據(jù)庫表m_user
Mybatis的五部曲:
總結(jié)
回顧JDBC的demo,JDBC中的問題,創(chuàng)建一個(gè)Mybatis示例,總結(jié)出重要的四個(gè)組件,以及每個(gè)組件的作用。
本文轉(zhuǎn)載自微信公眾號「Java后端技術(shù)全?!?,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系Java后端技術(shù)全棧公眾號。