本专栏的源码:https://gitee.com/dhi-chen-xiaoyang/yang-mybatis。
引言
在上一章中,我们实现了读取mapper配置并构造相关的mapper代理对象,读取mapper.xml文件中的sql信息等操作,现在,在上一章的基础上,我们接着开始链接数据库,通过封装JDBC,来实现我们数据库操作。
数据库准备
我们创建一个user表,用于后续进行测试,user表的结构如下图所示:
user表的内容如下:
添加User类
我们根据表结构,创建对应的user类,user类的结构如下:
package com.yang.mybatis.test;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private Integer id;
private String userName;
private String password;
private Integer age;
private Date createTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
JDBC基础操作
在使用jdbc之前,我们先引入mysql的依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
jdbc的操作,一般分为下面几个步骤:
1)加载JDBC驱动程序
2)创建数据库链接
3)创建一个preparedStatement
4)执行SQL语句
5)遍历数据集
6)处理异常,关闭JDBC对象资源
示例代码如下:
public static void main(String[] args) throws SQLException {
String url = "jdbc:mysql://localhost:3306/test?useSSL=false";
String username = "用户名";
String password = "密码";
Connection conn = null;
try {
// 1. 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 创建数据库链接
conn = DriverManager.getConnection(url, username, password);
conn.setAutoCommit(false);
// 3. 创建preparedStatement
String sql = "select user_name from user where id = ?";
PreparedStatement preparedStatement = conn.prepareStatement(sql);
preparedStatement.setInt(1, 1);
// 4. 执行sql
ResultSet resultSet = preparedStatement.executeQuery();
// 5. 遍历结果
if (resultSet.next()) {
System.out.println(resultSet.getString(1));
}
conn.commit();
} catch (SQLException | ClassNotFoundException e) {
conn.rollback();
e.printStackTrace();
} finally {
// 6. 释放连接
conn.close();
}
}
执行上述代码,结果如下:
将sql操作,收敛到SqlSession
在上一章,当我们调用mapper的方法时,最终是通过在MapperProxy中获取对应的MybatisStatement,然后打印出sql信息的,但是如果后续操作数据库是,也在MapperProxy中执行sql的话,不太方便管理。因此,我们添加一个IMybatisSqlSession类,后续对于数据库的操作,收敛到此类进行。
首先,我们添加IMybatisSqlSession:
package com.yang.mybatis.session;
public interface IMybatisSqlSession {
<T> T execute(String method, Object parameter);
<T> T getMapper(Class<T> type);
}
然后添加其默认实现:
package com.yang.mybatis.session;
import com.yang.mybatis.proxy.MapperProxyFactory;
public class DefaultMybatisSqlSession implements IMybatisSqlSession {
private MapperProxyFactory mapperProxyFactory;
public DefaultMybatisSqlSession(MapperProxyFactory mapperProxyFactory) {
this.mapperProxyFactory = mapperProxyFactory;
}
@Override
public <T> T execute(String method, Object parameter) {
return (T)("你被代理了!" + method + ",入参:" + parameter);
}
@Override
public <T> T getMapper(Class<T> type) {
return (T) mapperProxyFactory.newInstance(type, this);
}
}
添加IMybatisSqlSession的工厂接口
package com.yang.mybatis.session;
public interface MybatisSqlSessionFactory {
IMybatisSqlSession openSession();
}
添加工厂的默认实现:
package com.yang.mybatis.session;
import com.yang.mybatis.proxy.MapperProxyFactory;
public class DefaultMybatisSqlSessionFactory implements IMybatisSqlSessionFactory {
private MapperProxyFactory mapperProxyFactory;
public DefaultMybatisSqlSessionFactory(MapperProxyFactory mapperProxyFactory) {
this.mapperProxyFactory = mapperProxyFactory;
}
@Override
public IMybatisSqlSession openSession() {
return new DefaultMybatisSqlSession(mapperProxyFactory);
}
}
修改MapperProxyFactory,在新建MapperProxy的时候,将imybatisSqlSession传递给MapperProxy。
package com.yang.mybatis.proxy;
import com.yang.mybatis.config.MybatisConfiguration;
import com.yang.mybatis.session.IMybatisSqlSession;
import java.lang.reflect.Proxy;
public class MapperProxyFactory {
private MybatisConfiguration mybatisConfiguration;
public MapperProxyFactory(MybatisConfiguration mybatisConfiguration) {
this.mybatisConfiguration = mybatisConfiguration;
}
public Object newInstance(Class mapperType, IMybatisSqlSession mybatisSqlSession) {
MapperProxy mapperProxy = new MapperProxy(mapperType, mybatisSqlSession);
return Proxy.newProxyInstance(mapperType.getClassLoader(),
new Class[]{mapperType},
mapperProxy);
}
public MybatisConfiguration getMybatisConfiguration() {
return mybatisConfiguration;
}
public void setMybatisConfiguration(MybatisConfiguration mybatisConfiguration) {
this.mybatisConfiguration = mybatisConfiguration;
}
}
然后修改MapperProxy,在真正执行的时候,通过iMybatisSqlSession的execute,来执行sql操作,以此实现sql操作由iMybatisSqlSession来统一管理。
package com.yang.mybatis.proxy;
import com.yang.mybatis.session.IMybatisSqlSession;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MapperProxy<T> implements InvocationHandler {
private Class<T> mapperInterface;
private IMybatisSqlSession sqlSession;
public MapperProxy(Class<T> mapperInterface, IMybatisSqlSession mybatisSqlSession) {
this.mapperInterface = mapperInterface;
this.sqlSession = mybatisSqlSession;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
String methodName = this.mapperInterface.getName() + "." + method.getName();
return sqlSession.execute(methodName, args);
}
}
最后,我们添加sqlSession工厂类的创建者,通过创建者模式,来创建SqlSession工厂
package com.yang.mybatis.session;
import com.yang.mybatis.config.MybatisConfiguration;
import com.yang.mybatis.config.MybatisSqlStatement;
import com.yang.mybatis.config.parser.IMybatisConfigurationParser;
import com.yang.mybatis.config.parser.IMybatisMapperParser;
import com.yang.mybatis.proxy.MapperProxyFactory;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class MybatisSqlSessionFactoryBuilder {
private IMybatisConfigurationParser mybatisConfigurationParser;
private IMybatisMapperParser mybatisMapperParser;
private String configPath;
public IMybatisSqlSessionFactory buildSqlSessionFactory() {
if (configPath == null || configPath.isEmpty()) {
throw new RuntimeException("配置文件路径不合法==========");
}
if (this.mybatisMapperParser == null || this.mybatisConfigurationParser == null) {
throw new RuntimeException("缺少解析器=======");
}
MybatisConfiguration mybatisConfiguration = mybatisConfigurationParser.parser(configPath);
List<String> mapperPaths = mybatisConfiguration.getMapperPaths();
for (String mapperPath : mapperPaths) {
List<MybatisSqlStatement> mybatisSqlStatements = this.mybatisMapperParser.parseMapper(mapperPath);
Map<String, List<MybatisSqlStatement>> mapperNameGroupMap = mybatisSqlStatements.stream()
.collect(Collectors.groupingBy(MybatisSqlStatement::getNamespace));
for (Map.Entry<String, List<MybatisSqlStatement>> entry : mapperNameGroupMap.entrySet()) {
String mapperName = entry.getKey();
List<MybatisSqlStatement> sqlSessionList = entry.getValue();
mybatisConfiguration.putMybatisSqlStatementList(mapperName, sqlSessionList);
}
}
MapperProxyFactory mapperProxyFactory = new MapperProxyFactory(mybatisConfiguration);
return new DefaultMybatisSqlSessionFactory(mapperProxyFactory);
}
public MybatisSqlSessionFactoryBuilder setConfigPath(String configPath) {
this.configPath = configPath;
return this;
}
public MybatisSqlSessionFactoryBuilder setMybatisConfigurationParser(IMybatisConfigurationParser iMybatisConfigurationParser) {
this.mybatisConfigurationParser = iMybatisConfigurationParser;
return this;
}
public MybatisSqlSessionFactoryBuilder setMybatisMapperParser(IMybatisMapperParser iMybatisMapperParser) {
this.mybatisMapperParser = iMybatisMapperParser;
return this;
}
}
添加测试代码,进行测试:
package com.yang.mybatis.test;
import com.yang.mybatis.config.parser.XmlMybatisConfigurationParser;
import com.yang.mybatis.config.parser.XmlMybatisMapperParser;
import com.yang.mybatis.session.IMybatisSqlSession;
import com.yang.mybatis.session.IMybatisSqlSessionFactory;
import com.yang.mybatis.session.MybatisSqlSessionFactoryBuilder;
public class Main {
public static void main(String[] args) {
String configPath = "mybatis-config.xml";
IMybatisSqlSessionFactory mybatisSqlSessionFactory = new MybatisSqlSessionFactoryBuilder()
.setMybatisMapperParser(new XmlMybatisMapperParser())
.setMybatisConfigurationParser(new XmlMybatisConfigurationParser())
.setConfigPath(configPath)
.buildSqlSessionFactory();
IMybatisSqlSession mybatisSqlSession = mybatisSqlSessionFactory.openSession();
IUserMapper userMapper = mybatisSqlSession.getMapper(IUserMapper.class);
System.out.println(userMapper.queryUserName(1));
}
}
SqlSession获取执行方法对应的sql语句
首先,修改MybatisConfiguration,之前是将每一个mapper和他所拥有的MybatisSqlStatement列表关联起来,现在感觉粒度太大,因此,该为每一个mapper的方法和对应的MybatisSqlStatement关联。
package com.yang.mybatis.config;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MybatisConfiguration implements Serializable {
private Map<String, MybatisEnvironment> id2EnvironmentMap = new HashMap<>();
private MybatisEnvironment defaultMybatisEnvironment;
private List<String> mapperPaths = new ArrayList<>();
private Map<String, MybatisSqlStatement> mapperMethod2SqlStatementsMap = new HashMap<>();
public void putMapperMethod2MybatisSqlStatement(String mapperMethod, MybatisSqlStatement mybatisSqlStatement) {
this.mapperMethod2SqlStatementsMap.put(mapperMethod, mybatisSqlStatement);
}
public MybatisSqlStatement getMybatisSqlStatement(String mapperMethod) {
return this.mapperMethod2SqlStatementsMap.get(mapperMethod);
}
public Map<String, MybatisSqlStatement> getMapperMethod2SqlStatementsMap() {
return this.mapperMethod2SqlStatementsMap;
}
public void addEnvironment(String id, MybatisEnvironment mybatisEnvironment) {
this.id2EnvironmentMap.put(id, mybatisEnvironment);
}
public MybatisEnvironment getEnvironment(String id) {
return id2EnvironmentMap.get(id);
}
public MybatisEnvironment getDefaultMybatisEnvironment() {
return defaultMybatisEnvironment;
}
public void setDefaultMybatisEnvironment(MybatisEnvironment defaultMybatisEnvironment) {
this.defaultMybatisEnvironment = defaultMybatisEnvironment;
}
public void addMapperPath(String mapperPath) {
this.mapperPaths.add(mapperPath);
}
public List<String> getMapperPaths() {
return this.mapperPaths;
}
public List<MybatisEnvironment> getEnvironments() {
return new ArrayList<>(id2EnvironmentMap.values());
}
}
然后修改MybatisSqlSessionFactoryBuilder:
package com.yang.mybatis.session;
import com.yang.mybatis.config.MybatisConfiguration;
import com.yang.mybatis.config.MybatisSqlStatement;
import com.yang.mybatis.config.parser.IMybatisConfigurationParser;
import com.yang.mybatis.config.parser.IMybatisMapperParser;
import com.yang.mybatis.proxy.MapperProxyFactory;
import java.util.List;
public class MybatisSqlSessionFactoryBuilder {
private IMybatisConfigurationParser mybatisConfigurationParser;
private IMybatisMapperParser mybatisMapperParser;
private String configPath;
public IMybatisSqlSessionFactory buildSqlSessionFactory() {
if (configPath == null || configPath.isEmpty()) {
throw new RuntimeException("配置文件路径不合法==========");
}
if (this.mybatisMapperParser == null || this.mybatisConfigurationParser == null) {
throw new RuntimeException("缺少解析器=======");
}
MybatisConfiguration mybatisConfiguration = mybatisConfigurationParser.parser(configPath);
List<String> mapperPaths = mybatisConfiguration.getMapperPaths();
for (String mapperPath : mapperPaths) {
List<MybatisSqlStatement> mybatisSqlStatements = this.mybatisMapperParser.parseMapper(mapperPath);
for (MybatisSqlStatement mybatisSqlStatement : mybatisSqlStatements) {
String methodName = mybatisSqlStatement.getNamespace() + "." + mybatisSqlStatement.getId();
mybatisConfiguration.putMapperMethod2MybatisSqlStatement(methodName, mybatisSqlStatement);
}
}
MapperProxyFactory mapperProxyFactory = new MapperProxyFactory(mybatisConfiguration);
return new DefaultMybatisSqlSessionFactory(mapperProxyFactory);
}
public MybatisSqlSessionFactoryBuilder setConfigPath(String configPath) {
this.configPath = configPath;
return this;
}
public MybatisSqlSessionFactoryBuilder setMybatisConfigurationParser(IMybatisConfigurationParser iMybatisConfigurationParser) {
this.mybatisConfigurationParser = iMybatisConfigurationParser;
return this;
}
public MybatisSqlSessionFactoryBuilder setMybatisMapperParser(IMybatisMapperParser iMybatisMapperParser) {
this.mybatisMapperParser = iMybatisMapperParser;
return this;
}
}
修改DefaultMybatisSqlSession类,获取方法对应的MybatisStatement
package com.yang.mybatis.session;
import com.yang.mybatis.config.MybatisSqlStatement;
import com.yang.mybatis.proxy.MapperProxyFactory;
public class DefaultMybatisSqlSession implements IMybatisSqlSession {
private MapperProxyFactory mapperProxyFactory;
public DefaultMybatisSqlSession(MapperProxyFactory mapperProxyFactory) {
this.mapperProxyFactory = mapperProxyFactory;
}
@Override
public <T> T execute(String method, Object parameter) {
MybatisSqlStatement mybatisSqlStatement = this.mapperProxyFactory.getMybatisConfiguration().getMybatisSqlStatement(method);
return (T)("method:" + method + "sql:"+ mybatisSqlStatement.getSql() + ",入参:" + parameter);
}
@Override
public <T> T getMapper(Class<T> type) {
return (T) mapperProxyFactory.newInstance(type, this);
}
}
添加测试方法进行测试:
public static void main(String[] args) {
String configPath = "mybatis-config.xml";
MybatisSqlSessionFactory mybatisSqlSessionFactory = new MybatisSqlSessionFactoryBuilder()
.setMybatisMapperParser(new XmlMybatisMapperParser())
.setMybatisConfigurationParser(new XmlMybatisConfigurationParser())
.setConfigPath(configPath)
.buildSqlSessionFactory();
IMybatisSqlSession mybatisSqlSession = mybatisSqlSessionFactory.openSession();
IUserMapper userMapper = mybatisSqlSession.getMapper(IUserMapper.class);
System.out.println(userMapper.queryUserAge(1));
}
测试结果如下:
参考文章
https://blog.csdn.net/weixin_43520450/article/details/107230205