一文弄懂Mybatis中介者模式
一、前言
中介者模式的核心思想是將對象間的交互行為集中在一個中介者對象中,從而降低對象之間的耦合度。在mybatis中,中介者模式被廣泛應用于Session對象的創(chuàng)建和管理中。
具體來說,mybatis中的SqlSessionFactory就扮演了中介者的角色,它負責創(chuàng)建和管理SqlSession對象。SqlSession是mybatis中用于與數(shù)據(jù)庫交互的核心對象,而SqlSessionFactory則是創(chuàng)建SqlSession對象的工廠類。
當應用程序需要執(zhí)行一個操作(如查詢、添加或更新記錄)時,它將向SqlSessionFactory請求一個SqlSession對象。SqlSessionFactory根據(jù)需要的配置信息(如數(shù)據(jù)庫連接信息、事務管理器等)創(chuàng)建一個新的SqlSession對象,并將其返回給應用程序。
一旦應用程序獲得了SqlSession對象,它就可以使用SqlSession對象來執(zhí)行數(shù)據(jù)庫操作。當執(zhí)行完操作后,應用程序需要調(diào)用SqlSession的close()方法關閉資源,SqlSession將會被歸還給SqlSessionFactory進行資源回收。
通過將SqlSession對象的創(chuàng)建和管理職責交由SqlSessionFactory統(tǒng)一管理,不僅可以保證SqlSession對象的有效性和一致性,同時也可以避免重復創(chuàng)建和銷毀SqlSession對象的開銷,提高系統(tǒng)性能和穩(wěn)定性。
Mybatis是一個我項目開發(fā)中常用的非常優(yōu)秀的ORM框架,它可以幫我們快速、簡便地操作數(shù)據(jù)庫。本文將會按照Mybatis的原理手寫一個ORM框架,并通過利用中介者模式來實現(xiàn)JDBC方式操作數(shù)據(jù)庫的增強。
二、Mybatis工作原理
在開始實現(xiàn)ORM框架之前,我們需要先了解一下Mybatis的工作原理:

SqlSessionFactory:SqlSessionFactory是Mybatis的核心接口,它是一個工廠類,用于創(chuàng)建SqlSession對象。SqlSession是Mybatis的另一個核心接口,它提供了操作數(shù)據(jù)庫的方法和事務管理的功能。
Configuration:Configuration是Mybatis的配置類,它包含了Mybatis的所有配置,如數(shù)據(jù)源、映射關系等,用于生成SqlSessionFactory。
SqlSession:SqlSession是Mybatis的會話類,它與數(shù)據(jù)庫的連接是一一對應的,負責與數(shù)據(jù)庫進行交互。SqlSession中包含了一系列的操作數(shù)據(jù)庫的方法,如插入、更新、刪除、查詢等。
Mapper:Mapper是Mybatis的映射器,它定義了Java對象與SQL語句之間的映射關系,即將Java對象轉化為SQL語句,或?qū)QL語句轉化為Java對象。
Executor:Executor是Mybatis的執(zhí)行器,它負責執(zhí)行SQL語句,管理事務。Mybatis中有兩種類型的執(zhí)行器:SimpleExecutor和ReuseExecutor,SimpleExecutor會為每個SQL語句創(chuàng)建一個Statement對象,而ReuseExecutor則會復用Statement對象。
TypeHandler:TypeHandler是Mybatis的類型處理器,它用于將Java對象與數(shù)據(jù)庫中的數(shù)據(jù)類型進行轉換。Mybatis中內(nèi)置了許多常用的類型處理器,如StringTypeHandler、IntegerTypeHandler等,可以根據(jù)需要進行擴展。
三、手寫ORM框架
我們將按照Mybatis的工作原理來手寫一個ORM框架,該框架支持基本的增刪改查操作。

1. 創(chuàng)建Configuration類
首先,我們需要創(chuàng)建一個Configuration類,該類負責讀取配置文件并生成SqlSessionFactory對象。
public class Configuration {
private final Properties properties = new Properties(); // 存儲配置信息
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類
接下來,我們需要創(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類
然后,我們需要創(chuàng)建Executor類,該類負責執(zhí)行SQL語句,并返回結果。
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類
我們還需要創(chuàng)建MappedStatement類,該類保存了SQL語句和參數(shù)映射關系的信息。
public class MappedStatement {
private String statementId; // SQL語句的ID
private String sql; // SQL語句
private Class<?> parameterType; // 參數(shù)類型
private Class<?> resultType; // 返回值類型
// getter、setter方法省略...
}5. 創(chuàng)建MapperRegistry和MapperProxy類
最后,我們需要創(chuàng)建MapperRegistry和MapperProxy類,用于將Java對象與SQL語句之間進行映射。
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對象
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來執(zhí)行SQL語句。
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() {
// 關閉Executor等資源
}
}7. 創(chuàng)建MapperProxyFactory類
在創(chuàng)建MapperRegistry時,我們還需要創(chuàng)建一個MapperProxyFactory類,用于創(chuàng)建MapperProxy對象。
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)手寫了一個簡單的ORM框架,并且可以進行基本的增刪改查操作。
四、利用中介者模式增強JDBC方式操作數(shù)據(jù)庫
接下來,我們將利用中介者模式來增強JDBC方式操作數(shù)據(jù)庫的功能。
1. 創(chuàng)建DataSource類
首先,我們需要創(chuàng)建一個DataSource類,該類負責管理數(shù)據(jù)庫連接和釋放資源。
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類
然后,我們需要創(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類
我們還需要創(chuàng)建一個JdbcSqlSession類,該類繼承自DefaultSqlSession類,用于創(chuàng)建JdbcExecutor對象。
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() {
// 關閉DataSource等資源
}
}4. 創(chuàng)建JdbcMapperRegistry類
接下來,我們需要創(chuàng)建JdbcMapperRegistry類,該類繼承自MapperRegistry類,用于創(chuàng)建JdbcMapperProxyFactory對象。
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類
最后,我們需要創(chuàng)建JdbcMapperProxyFactory類,該類繼承自MapperProxyFactory類,用于創(chuàng)建JdbcMapperProxy對象。
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類
最后,我們還需要創(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對象
String statementId = mapperInterface.getName() + "." + method.getName();
MappedStatement mappedStatement = sqlSession.getConfiguration().getMappedStatement(statementId);
// 根據(jù)配置文件中的useCache屬性來判斷是否開啟緩存
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();
}
// 如果開啟了緩存,則將查詢結果添加到緩存中
if (mappedStatement.isUseCache()) {
// TODO: 將查詢結果添加到緩存中
}
return result;
}
}到此為止,我們利用中介者模式增強了JDBC方式操作數(shù)據(jù)庫的功能。通過使用中介者模式,我們可以讓JDBC操作數(shù)據(jù)庫更加方便快捷、靈活可擴展。
五、小結一下
Mybatis,這個神奇的ORM框架,讓我們在操作數(shù)據(jù)庫的道路上披荊斬棘。沒錯,你沒有聽錯,它就是那個可以幫我們快速、簡便地操作數(shù)據(jù)庫的好伙伴。不過話說回來,你有沒有想過,如果這個世界上沒有Mybatis,那我們程序員豈不是要像遠古時期惡劣的條件下編程,跟數(shù)據(jù)庫打交道會變得異常的困難。
但是別擔心,就算Mybatis哪天離我們而去了,我們也可以自己動手寫一個ORM框架,用中介者模式來增強JDBC方式操作數(shù)據(jù)庫,這樣,我們就可以輕松愉悅地跟數(shù)據(jù)庫玩耍啦。所以,讓我們手握鍵盤,放聲歌唱,繼承Mybatis的精髓,創(chuàng)造更加屬于我們自己的數(shù)據(jù)庫操作方式吧!
我在本文中全面講解了如何按照Mybatis的原理手寫一個ORM框架,并利用中介者模式增強JDBC方式操作數(shù)據(jù)庫的功能。在實際開發(fā)中,ORM框架可以幫助我們快速、簡便地操作數(shù)據(jù)庫,而中介者模式則可以讓我們的程序更加靈活可擴展。同時,在使用ORM框架時,我們需要注意管理數(shù)據(jù)庫連接和釋放資源等問題,以保證軟件系統(tǒng)的穩(wěn)定性和安全性。
到此這篇關于一文弄懂Mybatis中介者模式的文章就介紹到這了,更多相關Mybatis中介者模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot整合SpringSession實現(xiàn)分布式登錄詳情
這篇文章主要介紹了SpringBoot整合SpringSession實現(xiàn)分布式登錄詳情,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下2022-08-08
Spring事件監(jiān)聽機制之@EventListener實現(xiàn)方式詳解
這篇文章主要介紹了Spring事件監(jiān)聽機制之@EventListener實現(xiàn)方式詳解,ApplicationContext的refresh方法還是初始化了SimpleApplicationEventMulticaster,發(fā)送事件式還是先獲取ResolvableType類型,再獲取發(fā)送監(jiān)聽列表,需要的朋友可以參考下2023-12-12
java常見報錯:Array?Out?of?Bounds兩種解決辦法
這篇文章主要給大家介紹了關于java報錯Array?Out?of?Bounds的兩種解決辦法,Array out of bounds錯誤表示你嘗試訪問數(shù)組中不存在的索引,即索引小于零或者大于等于數(shù)組的大小,文中通過代碼將解決的辦法介紹的非常詳細,需要的朋友可以參考下2024-08-08
詳解java連接mysql數(shù)據(jù)庫的五種方式
這篇文章主要介紹了詳解java連接mysql數(shù)據(jù)庫的五種方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-11-11

