一文弄懂Mybatis中介者模式
一、前言
中介者模式的核心思想是將對(duì)象間的交互行為集中在一個(gè)中介者對(duì)象中,從而降低對(duì)象之間的耦合度。在mybatis中,中介者模式被廣泛應(yīng)用于Session對(duì)象的創(chuàng)建和管理中。
具體來(lái)說(shuō),mybatis中的SqlSessionFactory就扮演了中介者的角色,它負(fù)責(zé)創(chuàng)建和管理SqlSession對(duì)象。SqlSession是mybatis中用于與數(shù)據(jù)庫(kù)交互的核心對(duì)象,而SqlSessionFactory則是創(chuàng)建SqlSession對(duì)象的工廠類。
當(dāng)應(yīng)用程序需要執(zhí)行一個(gè)操作(如查詢、添加或更新記錄)時(shí),它將向SqlSessionFactory請(qǐng)求一個(gè)SqlSession對(duì)象。SqlSessionFactory根據(jù)需要的配置信息(如數(shù)據(jù)庫(kù)連接信息、事務(wù)管理器等)創(chuàng)建一個(gè)新的SqlSession對(duì)象,并將其返回給應(yīng)用程序。
一旦應(yīng)用程序獲得了SqlSession對(duì)象,它就可以使用SqlSession對(duì)象來(lái)執(zhí)行數(shù)據(jù)庫(kù)操作。當(dāng)執(zhí)行完操作后,應(yīng)用程序需要調(diào)用SqlSession的close()方法關(guān)閉資源,SqlSession將會(huì)被歸還給SqlSessionFactory進(jìn)行資源回收。
通過(guò)將SqlSession對(duì)象的創(chuàng)建和管理職責(zé)交由SqlSessionFactory統(tǒng)一管理,不僅可以保證SqlSession對(duì)象的有效性和一致性,同時(shí)也可以避免重復(fù)創(chuàng)建和銷毀SqlSession對(duì)象的開銷,提高系統(tǒng)性能和穩(wěn)定性。
Mybatis是一個(gè)我項(xiàng)目開發(fā)中常用的非常優(yōu)秀的ORM框架,它可以幫我們快速、簡(jiǎn)便地操作數(shù)據(jù)庫(kù)。本文將會(huì)按照Mybatis的原理手寫一個(gè)ORM框架,并通過(guò)利用中介者模式來(lái)實(shí)現(xiàn)JDBC方式操作數(shù)據(jù)庫(kù)的增強(qiáng)。
二、Mybatis工作原理
在開始實(shí)現(xiàn)ORM框架之前,我們需要先了解一下Mybatis的工作原理:
SqlSessionFactory:SqlSessionFactory是Mybatis的核心接口,它是一個(gè)工廠類,用于創(chuàng)建SqlSession對(duì)象。SqlSession是Mybatis的另一個(gè)核心接口,它提供了操作數(shù)據(jù)庫(kù)的方法和事務(wù)管理的功能。
Configuration:Configuration是Mybatis的配置類,它包含了Mybatis的所有配置,如數(shù)據(jù)源、映射關(guān)系等,用于生成SqlSessionFactory。
SqlSession:SqlSession是Mybatis的會(huì)話類,它與數(shù)據(jù)庫(kù)的連接是一一對(duì)應(yīng)的,負(fù)責(zé)與數(shù)據(jù)庫(kù)進(jìn)行交互。SqlSession中包含了一系列的操作數(shù)據(jù)庫(kù)的方法,如插入、更新、刪除、查詢等。
Mapper:Mapper是Mybatis的映射器,它定義了Java對(duì)象與SQL語(yǔ)句之間的映射關(guān)系,即將Java對(duì)象轉(zhuǎn)化為SQL語(yǔ)句,或?qū)QL語(yǔ)句轉(zhuǎn)化為Java對(duì)象。
Executor:Executor是Mybatis的執(zhí)行器,它負(fù)責(zé)執(zhí)行SQL語(yǔ)句,管理事務(wù)。Mybatis中有兩種類型的執(zhí)行器:SimpleExecutor和ReuseExecutor,SimpleExecutor會(huì)為每個(gè)SQL語(yǔ)句創(chuàng)建一個(gè)Statement對(duì)象,而ReuseExecutor則會(huì)復(fù)用Statement對(duì)象。
TypeHandler:TypeHandler是Mybatis的類型處理器,它用于將Java對(duì)象與數(shù)據(jù)庫(kù)中的數(shù)據(jù)類型進(jìn)行轉(zhuǎn)換。Mybatis中內(nèi)置了許多常用的類型處理器,如StringTypeHandler、IntegerTypeHandler等,可以根據(jù)需要進(jìn)行擴(kuò)展。
三、手寫ORM框架
我們將按照Mybatis的工作原理來(lái)手寫一個(gè)ORM框架,該框架支持基本的增刪改查操作。
1. 創(chuàng)建Configuration類
首先,我們需要?jiǎng)?chuàng)建一個(gè)Configuration類,該類負(fù)責(zé)讀取配置文件并生成SqlSessionFactory對(duì)象。
public class Configuration { private final Properties properties = new Properties(); // 存儲(chǔ)配置信息 public Configuration(String configLocation) { InputStream is = null; try { is = Resources.getResourceAsStream(configLocation); properties.load(is); } catch (IOException e) { throw new RuntimeException("Config file not found!"); } finally { try { if (is != null) { is.close(); } } catch (IOException e) { e.printStackTrace(); } } } public SqlSessionFactory buildSqlSessionFactory() { return new DefaultSqlSessionFactory(this); } // getter 方法省略... }
2. 創(chuàng)建SqlSessionFactory和SqlSession類
接下來(lái),我們需要?jiǎng)?chuàng)建SqlSessionFactory和SqlSession類。
public interface SqlSessionFactory { SqlSession openSession(); }
public interface SqlSession { int insert(String statementId, Object parameter); int update(String statementId, Object parameter); int delete(String statementId, Object parameter); <T> T selectOne(String statementId, Object parameter); <T> List<T> selectList(String statementId, Object parameter); void close(); }
3. 創(chuàng)建Executor類
然后,我們需要?jiǎng)?chuàng)建Executor類,該類負(fù)責(zé)執(zhí)行SQL語(yǔ)句,并返回結(jié)果。
public interface Executor { int insert(MappedStatement mappedStatement, Object parameter); int update(MappedStatement mappedStatement, Object parameter); int delete(MappedStatement mappedStatement, Object parameter); <T> T selectOne(MappedStatement mappedStatement, Object parameter); <E> List<E> selectList(MappedStatement mappedStatement, Object parameter); }
4. 創(chuàng)建MappedStatement類
我們還需要?jiǎng)?chuàng)建MappedStatement類,該類保存了SQL語(yǔ)句和參數(shù)映射關(guān)系的信息。
public class MappedStatement { private String statementId; // SQL語(yǔ)句的ID private String sql; // SQL語(yǔ)句 private Class<?> parameterType; // 參數(shù)類型 private Class<?> resultType; // 返回值類型 // getter、setter方法省略... }
5. 創(chuàng)建MapperRegistry和MapperProxy類
最后,我們需要?jiǎng)?chuàng)建MapperRegistry和MapperProxy類,用于將Java對(duì)象與SQL語(yǔ)句之間進(jìn)行映射。
public class MapperRegistry { private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new ConcurrentHashMap<>(); public <T> void addMapper(Class<T> type) { knownMappers.put(type, new MapperProxyFactory<>(type)); } public <T> T getMapper(SqlSession sqlSession, Class<T> type) { MapperProxyFactory<?> mapperProxyFactory = knownMappers.get(type); if (mapperProxyFactory == null) { throw new RuntimeException("Mapper not found: " + type); } return (T) mapperProxyFactory.newInstance(sqlSession); } }
public class MapperProxy<T> implements InvocationHandler { private final SqlSession sqlSession; private final Class<T> mapperInterface; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 獲取MappedStatement對(duì)象 String statementId = mapperInterface.getName() + "." + method.getName(); MappedStatement mappedStatement = sqlSession.getConfiguration().getMappedStatement(statementId); // 調(diào)用Executor執(zhí)行SQL Object result = null; switch (mappedStatement.getSqlCommandType()) { case INSERT: result = sqlSession.insert(statementId, args[0]); break; case UPDATE: result = sqlSession.update(statementId, args[0]); break; case DELETE: result = sqlSession.delete(statementId, args[0]); break; case SELECT: if (method.getReturnType() == List.class) { result = sqlSession.selectList(statementId, args[0]); } else { result = sqlSession.selectOne(statementId, args[0]); } break; default: throw new RuntimeException("Unknown SqlCommandType: " + mappedStatement.getSqlCommandType()); } return result; } }
6. 在SqlSession中調(diào)用Executor
最后,在SqlSession的方法中,我們需要調(diào)用Executor來(lái)執(zhí)行SQL語(yǔ)句。
public class DefaultSqlSession implements SqlSession { private final Executor executor; private final Configuration configuration; public DefaultSqlSession(Configuration configuration) { this.executor = new SimpleExecutor(configuration); this.configuration = configuration; } @Override public int insert(String statementId, Object parameter) { MappedStatement mappedStatement = configuration.getMappedStatement(statementId); return executor.insert(mappedStatement, parameter); } @Override public int update(String statementId, Object parameter) { MappedStatement mappedStatement = configuration.getMappedStatement(statementId); return executor.update(mappedStatement, parameter); } @Override public int delete(String statementId, Object parameter) { MappedStatement mappedStatement = configuration.getMappedStatement(statementId); return executor.delete(mappedStatement, parameter); } @Override public <T> T selectOne(String statementId, Object parameter) { MappedStatement mappedStatement = configuration.getMappedStatement(statementId); return executor.selectOne(mappedStatement, parameter); } @Override public <T> List<T> selectList(String statementId, Object parameter) { MappedStatement mappedStatement = configuration.getMappedStatement(statementId); return executor.selectList(mappedStatement, parameter); } @Override public void close() { // 關(guān)閉Executor等資源 } }
7. 創(chuàng)建MapperProxyFactory類
在創(chuàng)建MapperRegistry時(shí),我們還需要?jiǎng)?chuàng)建一個(gè)MapperProxyFactory類,用于創(chuàng)建MapperProxy對(duì)象。
public class MapperProxyFactory<T> { private final Class<T> mapperInterface; public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public T newInstance(SqlSession sqlSession) { return (T) Proxy.newProxyInstance( mapperInterface.getClassLoader(), new Class[]{mapperInterface}, new MapperProxy<>(sqlSession, mapperInterface)); } }
到此為止,我們已經(jīng)手寫了一個(gè)簡(jiǎn)單的ORM框架,并且可以進(jìn)行基本的增刪改查操作。
四、利用中介者模式增強(qiáng)JDBC方式操作數(shù)據(jù)庫(kù)
接下來(lái),我們將利用中介者模式來(lái)增強(qiáng)JDBC方式操作數(shù)據(jù)庫(kù)的功能。
1. 創(chuàng)建DataSource類
首先,我們需要?jiǎng)?chuàng)建一個(gè)DataSource類,該類負(fù)責(zé)管理數(shù)據(jù)庫(kù)連接和釋放資源。
public class DataSource { private String url; private String username; private String password; private static final ThreadLocal<Connection> connectionHolder = new ThreadLocal<>(); public DataSource(String url, String username, String password) { this.url = url; this.username = username; this.password = password; } public Connection getConnection() throws SQLException { Connection connection = connectionHolder.get(); if (connection == null) { connection = DriverManager.getConnection(url, username, password); connectionHolder.set(connection); } return connection; } public void releaseConnection(Connection connection) throws SQLException { if (connection != null && !connection.isClosed()) { connection.close(); } connectionHolder.remove(); } }
2. 創(chuàng)建JdbcExecutor類
然后,我們需要?jiǎng)?chuàng)建JdbcExecutor類,該類繼承自Executor類,用于執(zhí)行JDBC操作。
public class JdbcExecutor extends Executor { private final DataSource dataSource; public JdbcExecutor(Configuration configuration, DataSource dataSource) { super(configuration); this.dataSource = dataSource; } @Override public int insert(MappedStatement mappedStatement, Object parameter) { Connection connection = null; PreparedStatement statement = null; try { connection = dataSource.getConnection(); statement = connection.prepareStatement(mappedStatement.getSql()); statement.setObject(1, parameter); return statement.executeUpdate(); } catch (SQLException e) { throw new RuntimeException(e); } finally { try { if (statement != null) { statement.close(); } if (connection != null) { dataSource.releaseConnection(connection); } } catch (SQLException e) { e.printStackTrace(); } } } @Override public int update(MappedStatement mappedStatement, Object parameter) { // 類似insert方法,此處省略... } @Override public int delete(MappedStatement mappedStatement, Object parameter) { // 類似insert方法,此處省略... } @Override public <T> T selectOne(MappedStatement mappedStatement, Object parameter) { // 類似insert方法,此處省略... } @Override public <E> List<E> selectList(MappedStatement mappedStatement, Object parameter) { // 類似insert方法,此處省略... } }
3. 創(chuàng)建JdbcSqlSession類
我們還需要?jiǎng)?chuàng)建一個(gè)JdbcSqlSession類,該類繼承自DefaultSqlSession類,用于創(chuàng)建JdbcExecutor對(duì)象。
public class JdbcSqlSession extends DefaultSqlSession { private final DataSource dataSource; public JdbcSqlSession(Configuration configuration, DataSource dataSource) { super(configuration); this.executor = new JdbcExecutor(configuration, dataSource); this.dataSource = dataSource; } @Override public void close() { // 關(guān)閉DataSource等資源 } }
4. 創(chuàng)建JdbcMapperRegistry類
接下來(lái),我們需要?jiǎng)?chuàng)建JdbcMapperRegistry類,該類繼承自MapperRegistry類,用于創(chuàng)建JdbcMapperProxyFactory對(duì)象。
public class JdbcMapperRegistry extends MapperRegistry { private final DataSource dataSource; public JdbcMapperRegistry(Configuration configuration, DataSource dataSource) { super(); this.dataSource = dataSource; } @Override public <T> void addMapper(Class<T> type) { super.addMapper(type); JdbcMapperProxyFactory<?> mapperProxyFactory = new JdbcMapperProxyFactory<>(dataSource, type); knownMappers.put(type, mapperProxyFactory); } }
5. 創(chuàng)建JdbcMapperProxyFactory類
最后,我們需要?jiǎng)?chuàng)建JdbcMapperProxyFactory類,該類繼承自MapperProxyFactory類,用于創(chuàng)建JdbcMapperProxy對(duì)象。
public class JdbcMapperProxyFactory<T> extends MapperProxyFactory<T> { private final DataSource dataSource; public JdbcMapperProxyFactory(DataSource dataSource, Class<T> mapperInterface) { super(mapperInterface); this.dataSource = dataSource; } @Override public T newInstance(SqlSession sqlSession) { return (T) Proxy.newProxyInstance( mapperInterface.getClassLoader(), new Class[]{mapperInterface}, new JdbcMapperProxy<>(sqlSession, dataSource, mapperInterface)); } }
6. 創(chuàng)建JdbcMapperProxy類
最后,我們還需要?jiǎng)?chuàng)建JdbcMapperProxy類,該類繼承自MapperProxy類,用于調(diào)用JdbcSqlSession的方法。
public class JdbcMapperProxy<T> extends MapperProxy<T> { private final DataSource dataSource; public JdbcMapperProxy(SqlSession sqlSession, DataSource dataSource, Class<T> mapperInterface) { super(sqlSession, mapperInterface); this.dataSource = dataSource; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 如果是Object類的方法,則直接調(diào)用父類的方法 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } // 獲取MappedStatement對(duì)象 String statementId = mapperInterface.getName() + "." + method.getName(); MappedStatement mappedStatement = sqlSession.getConfiguration().getMappedStatement(statementId); // 根據(jù)配置文件中的useCache屬性來(lái)判斷是否開啟緩存 if (mappedStatement.isUseCache()) { // TODO: 如果開啟了緩存,先從緩存中查找數(shù)據(jù),如果不存在再調(diào)用JdbcSqlSession中的方法 } // 調(diào)用JdbcSqlSession的方法 Object result; JdbcSqlSession jdbcSqlSession = new JdbcSqlSession(sqlSession.getConfiguration(), dataSource); try { result = method.invoke(jdbcSqlSession.getMapper(mapperInterface), args); } catch (Exception e) { throw new RuntimeException(e); } finally { jdbcSqlSession.close(); } // 如果開啟了緩存,則將查詢結(jié)果添加到緩存中 if (mappedStatement.isUseCache()) { // TODO: 將查詢結(jié)果添加到緩存中 } return result; } }
到此為止,我們利用中介者模式增強(qiáng)了JDBC方式操作數(shù)據(jù)庫(kù)的功能。通過(guò)使用中介者模式,我們可以讓JDBC操作數(shù)據(jù)庫(kù)更加方便快捷、靈活可擴(kuò)展。
五、小結(jié)一下
Mybatis,這個(gè)神奇的ORM框架,讓我們?cè)诓僮鲾?shù)據(jù)庫(kù)的道路上披荊斬棘。沒錯(cuò),你沒有聽錯(cuò),它就是那個(gè)可以幫我們快速、簡(jiǎn)便地操作數(shù)據(jù)庫(kù)的好伙伴。不過(guò)話說(shuō)回來(lái),你有沒有想過(guò),如果這個(gè)世界上沒有Mybatis,那我們程序員豈不是要像遠(yuǎn)古時(shí)期惡劣的條件下編程,跟數(shù)據(jù)庫(kù)打交道會(huì)變得異常的困難。
但是別擔(dān)心,就算Mybatis哪天離我們而去了,我們也可以自己動(dòng)手寫一個(gè)ORM框架,用中介者模式來(lái)增強(qiáng)JDBC方式操作數(shù)據(jù)庫(kù),這樣,我們就可以輕松愉悅地跟數(shù)據(jù)庫(kù)玩耍啦。所以,讓我們手握鍵盤,放聲歌唱,繼承Mybatis的精髓,創(chuàng)造更加屬于我們自己的數(shù)據(jù)庫(kù)操作方式吧!
我在本文中全面講解了如何按照Mybatis的原理手寫一個(gè)ORM框架,并利用中介者模式增強(qiáng)JDBC方式操作數(shù)據(jù)庫(kù)的功能。在實(shí)際開發(fā)中,ORM框架可以幫助我們快速、簡(jiǎn)便地操作數(shù)據(jù)庫(kù),而中介者模式則可以讓我們的程序更加靈活可擴(kuò)展。同時(shí),在使用ORM框架時(shí),我們需要注意管理數(shù)據(jù)庫(kù)連接和釋放資源等問題,以保證軟件系統(tǒng)的穩(wěn)定性和安全性。
到此這篇關(guān)于一文弄懂Mybatis中介者模式的文章就介紹到這了,更多相關(guān)Mybatis中介者模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot集成Kaptcha驗(yàn)證碼的詳細(xì)過(guò)程
Kaptcha是一個(gè)強(qiáng)大而靈活的Java驗(yàn)證碼生成庫(kù),通過(guò)合理的配置和使用,它可以有效地提高web應(yīng)用的安全性,防止自動(dòng)化程序的濫用,這篇文章主要介紹了SpringBoot集成Kaptcha驗(yàn)證碼,需要的朋友可以參考下2024-07-07SpringBoot整合SpringSession實(shí)現(xiàn)分布式登錄詳情
這篇文章主要介紹了SpringBoot整合SpringSession實(shí)現(xiàn)分布式登錄詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-08-08Spring事件監(jiān)聽機(jī)制之@EventListener實(shí)現(xiàn)方式詳解
這篇文章主要介紹了Spring事件監(jiān)聽機(jī)制之@EventListener實(shí)現(xiàn)方式詳解,ApplicationContext的refresh方法還是初始化了SimpleApplicationEventMulticaster,發(fā)送事件式還是先獲取ResolvableType類型,再獲取發(fā)送監(jiān)聽列表,需要的朋友可以參考下2023-12-12JAVA中while循環(huán)的使用與注意事項(xiàng)
這篇文章主要介紹了while循環(huán)在編程中的應(yīng)用,包括其基本結(jié)構(gòu)、語(yǔ)句示例、適用場(chǎng)景以及注意事項(xiàng),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-01-01java常見報(bào)錯(cuò):Array?Out?of?Bounds兩種解決辦法
這篇文章主要給大家介紹了關(guān)于java報(bào)錯(cuò)Array?Out?of?Bounds的兩種解決辦法,Array out of bounds錯(cuò)誤表示你嘗試訪問數(shù)組中不存在的索引,即索引小于零或者大于等于數(shù)組的大小,文中通過(guò)代碼將解決的辦法介紹的非常詳細(xì),需要的朋友可以參考下2024-08-08詳解java連接mysql數(shù)據(jù)庫(kù)的五種方式
這篇文章主要介紹了詳解java連接mysql數(shù)據(jù)庫(kù)的五種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11