MyBatis-step02

这是一个流程,

  • 通过 SqlSessionFactory 开启一个 SqlSession
  • SqlSession 里有成员变量 MapperRegistry
  • MapperRegistry 里面通过包路径扫描,得到了一个 map
  • 这个 map 封装了接口,跟它的代理类工厂
  • 可以通过 get 的方式获得代理类工厂,用工厂的静态方法 new 出来一个代理对象
  • 使用工厂模式可以屏蔽创建细节,延迟创建过程

![image-20240818141349865](/Users/sudong/Library/Application Support/typora-user-images/image-20240818141349865.png)

目前这个接口中对于数据库的操作仅仅只提供了 selectOne,后续还会有相应其他方法的定义

目录结构和代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mybatis-step-02
└── src
├── main
│ └── java
│ └── cn.bugstack.mybatis
│ ├── binding
│ │ ├── MapperProxy.java
│ │ ├── MapperProxyFactory.java
│ │ └── MapperRegistry.java
│ └── session
│ ├── defaults
│ │ ├── DefaultSqlSession.java
│ │ └── DefaultSqlSessionFactory.java
│ ├── SqlSession.java
│ └── SqlSessionFactory.java
└── test
└── java
└── cn.bugstack.mybatis.test.dao
├── dao
│ ├── ISchoolDao.java
│ └── IUserDao.java
└── ApiTest.java

MapperProxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MapperProxy<T> implements InvocationHandler, Serializable {

private static final long serialVersionUID = -6424540398559729838L;

// 所有的数据库操作语句都是通过 接口名称+方法名称作为 key,操作逻辑作为 value
// 在反射中调用就是获取对应的操作直接执行并返回结果
private 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 {
// Object 类里的方法不代理
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
return "你的被代理了!";
}
}
}

MapperProxyFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MapperProxyFactory<T> {

private final Class<T> mapperInterface;

public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}

public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface);
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}

}

MapperRegistry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class MapperRegistry {

/**
* 将已添加的映射器代理加入到 HashMap
*/
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

/**
* 根据类型,返回工厂,再用工厂生成代理对象
* @param type
* @param sqlSession
* @return
* @param <T>
*/
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new RuntimeException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new RuntimeException("Error getting mapper instance. Cause: " + e, e);
}
}

/**
* 把 type 类型的添加到 knownMappers 这个 map 里
*/
public <T> void addMapper(Class<T> type) {
/* Mapper 必须是接口才会注册 */
if (type.isInterface()) {
if (hasMapper(type)) {
// 如果重复添加了,报错
throw new RuntimeException("Type " + type + " is already known to the MapperRegistry.");
}
// 注册映射器代理工厂
knownMappers.put(type, new MapperProxyFactory<>(type));
}
}

/**
* 判断是否存在过
*/
private <T> boolean hasMapper(Class<T> type) {
return knownMappers.containsKey(type);
}

/**
* 把包下的所有的类添加到 knownMappers 里
* @param packageName
*/
public void addMappers(String packageName) {
// 用于扫描指定包名下的所有类,并返回这些类的集合
Set<Class<?>> mapperSet = ClassScanner.scanPackage(packageName);
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}

}

DefaultSqlSession

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class DefaultSqlSession implements SqlSession {

/**
* 映射器注册机
*/
private final MapperRegistry mapperRegistry;

DefaultSqlSession(MapperRegistry mapperRegistry){
this.mapperRegistry = mapperRegistry;
}

@Override
public <T> T selectOne(String statement) {
return (T) ("你被代理了!" + "方法:" + statement);
}

@Override
public <T> T selectOne(String statement, Object parameter) {
return (T) ("你被代理了!" + "方法:" + statement + " 入参:" + parameter);
}

@Override
public <T> T getMapper(Class<T> type) {
return mapperRegistry.getMapper(type, this);
}

}

DefaultSqlSessionFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DefaultSqlSessionFactory implements SqlSessionFactory {

private final MapperRegistry mapperRegistry;

public DefaultSqlSessionFactory(MapperRegistry mapperRegistry) {
this.mapperRegistry = mapperRegistry;
}

@Override
public SqlSession openSession() {
return new DefaultSqlSession(mapperRegistry);
}

}

SqlSession

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public interface SqlSession {

/**
* Retrieve a single row mapped from the statement key
* 根据指定的SqlID获取一条记录的封装对象
*
* @param <T> the returned object type 封装之后的对象类型
* @param statement sqlID
* @return Mapped object 封装之后的对象
*/
<T> T selectOne(String statement);

/**
* Retrieve a single row mapped from the statement key and parameter.
* 根据指定的SqlID获取一条记录的封装对象,只不过这个方法容许我们可以给sql传递一些参数
* 一般在实际使用中,这个参数传递的是pojo,或者Map或者ImmutableMap
*
* @param <T> the returned object type
* @param statement Unique identifier matching the statement to use.
* @param parameter A parameter object to pass to the statement.
* @return Mapped object
*/
<T> T selectOne(String statement, Object parameter);

/**
* Retrieves a mapper.
* 得到映射器,这个巧妙的使用了泛型,使得类型安全
*
* @param <T> the mapper type
* @param type Mapper interface class
* @return a mapper bound to this SqlSession
*/
<T> T getMapper(Class<T> type);

}

SqlSessionFactory

1
2
3
4
5
6
7
8
9
10
public interface SqlSessionFactory {

/**
* 打开一个 session
* @return SqlSession
*/
SqlSession openSession();

}

Dao 层接口

1
2
3
4
5
6
7
8
9
10
11
12
public interface ISchoolDao {

String querySchoolName(String uId);

}
public interface IUserDao {

String queryUserName(String uId);

Integer queryUserAge(String uId);

}

测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void test_MapperProxyFactory() {
// 1. 注册 Mapper
MapperRegistry registry = new MapperRegistry();
registry.addMappers("cn.bugstack.mybatis.test.dao");

// 2. 从 SqlSession 工厂获取 Session
SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(registry);
SqlSession sqlSession = sqlSessionFactory.openSession();

// 3. 获取映射器对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);

// 4. 测试验证
String res = userDao.queryUserName("10001");
logger.info("测试结果:{}", res);
}

答疑

1
2
3
4
5
6
7
8
9
/**
* Retrieves a mapper.
* 得到映射器,这个巧妙的使用了泛型,使得类型安全
*
* @param <T> the mapper type
* @param type Mapper interface class
* @return a mapper bound to this SqlSession
*/
<T> T getMapper(Class<T> type);

如何理解 “巧妙地使用了泛型,使得类型安全”

在这个方法里,<T> 表示这是一个泛型方法,其中传入的参数是 Class<T> 这说明参数的类型与返回值的类型应该相等

类型安全意味着编译器会在编译时进行类型检查,以防止类型错误,通过泛型,getMapper 方法能够确保返回的对象类型与调用者期望的类型一致,避免了强制类型转换

在调用的时候可以这样

1
UserMapper userMapper = sqlSesson.getMapepr(UserMapper.class);

与非泛型方法对比

1
public Object getMapper(Class<?> type);

在调用的时候需要强制类型转换,编译器无法验证类型转换是否正确,可能在运行时抛出 ClassCastException

1
UserMapper userMapper = (UserMapper) sqlSession.getMapper(UserMapper.class);

本篇文章是在小傅哥的基础上,添加了自己的理解写下的
小傅哥博客:https://bugstack.cn


MyBatis-step02
http://showyoubug.cn/2024/07/22/MyBatis-step02/
作者
Dong Su
发布于
2024年7月22日
许可协议