本章节主要是解决通过xml配置可处理sql语句,所以最主要的就是解析xml,一个解析并放入对应数据源对象DataSourceFactory和事务对象TransactionFactory,一个处理Sql并放入对应的Sql类对象MapperStateMent,最终得到这些基础信息以后怎么使用呢,数据源肯定是要连接数据库最后才能执行各种库操作,所以就需要DataSourceFactory,那么想要执行就需要Sql语句,就需要MapperStateMent,所以贯穿所有中心枢纽就是Configuration,既会得到DataSourceFactory也会得到MapperStateMent,通过此传入SqlSeeion里,这样就能执行sql语句啦
1.类图

2.代码
Transaction类:定义事务接口,定义获取连接、提交、回滚、关闭方法,这样就可以不同的事务方式由不同的具体实现类去实现。
package df.middleware.mybatis.transaction
/**
* @Author df
* @Date 2022/5/26 11:19
* @Version 1.0
* 事务接口,定义获取连接,提交,回滚,关闭连接
*/
public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
}
JdbcTransaction类:为Transaction的具体实现类,封装JDBC方式的事务实现,包括获取连接以及提交回滚,关闭。
package df.middleware.mybatis.transaction.jdbc
/**
* @Author df
* @Date 2022/5/26 11:22
* @Version 1.0
* JDBC事务,直接利用JDBC的commit,rollback,依赖于数据源获得的连接来管理事务范围
*/
public class JdbcTransaction implements Transaction {
protected Connection connection;
protected DataSource dataSource;
// 事务隔离级别
protected TransactionIsolationLevel level = TransactionIsolationLevel.NONE;
protected boolean autoCommit;
public JdbcTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
this.dataSource = dataSource;
this.level = level;
this.autoCommit = autoCommit;
}
public JdbcTransaction(Connection connection) {
this.connection = connection;
}
@Override
public Connection getConnection() throws SQLException {
connection = dataSource.getConnection();
connection.setTransactionIsolation(level.getLevel());
connection.setAutoCommit(autoCommit);
return connection;
}
@Override
public void commit() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
connection.commit();
}
}
@Override
public void rollback() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
connection.rollback();
}
}
@Override
public void close() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
connection.close();
}
}
TransactionFactory类:主要目的定义从事务工厂能够获得事务基本功能Transaction
package df.middleware.mybatis.transaction
/**
* @Author df
* @Date 2022/5/26 14:13
* @Version 1.0
* 事务工厂
*/
public interface TransactionFactory {
/**
* 根据 Connection 创建Transaction
*
* @param conn Existing database connection
* @return Transaction
*/
Transaction newTransaction(Connection conn);
/**
*根据数据源和事务隔离级别创建Transaction
* @param dataSource
*/
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
JdbcTransactionFactory类:为TransactionFactory的实现类,通过此种实现可以已不同方式获取事务,此实现是通过JDBC获取事务。
package df.middleware.mybatis.transaction.jdbc
/**
* @Author df
* @Date 2022/5/26 14:27
* @Version 1.0
* JdbcTransaction 工厂
*/
public class JdbcTransactionFactory implements TransactionFactory {
@Override
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
@Override
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(dataSource, level, autoCommit);
}
}
Environment类:环境类,mybatis的提供数据库连接的环境,Environment类主要是通过xml的用户配置数据库环境信息再映射到此对象中使用,可配置多个数据库环境,根据环境id区分,除此之外还有事务的工厂对象,数据库的数据源等等。
package df.middleware.mybatis.mapping
public class Environment {
// 环境id,不同的id表示不同的环境
private final String id;
// 创建事务的工厂
private final TransactionFactory transactionFactory;
// 数据源
private final DataSource dataSource;
public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {
this.id = id;
this.transactionFactory = transactionFactory;
this.dataSource = dataSource;
}
public static class Builder {
private String id;
private TransactionFactory transactionFactory;
private DataSource dataSource;
public Builder(String id) {
this.id = id;
}
public Builder transactionFactory(TransactionFactory transactionFactory) {
this.transactionFactory = transactionFactory;
return this;
}
public Builder dataSource(DataSource dataSource) {
this.dataSource = dataSource;
return this;
}
public String id() {
return this.id;
}
public Environment Builder() {
return new Environment(this.id, this.transactionFactory, this.dataSource);
}
}
// 省略set/get
}
Configuration类:Configuration这一章节添加如下,添加了Environment对象,最终在xml里通过Configuration的setEnvironment得到环境信息,再注册机里添加jdbc和druid事务工厂,这样在xml里解析时可以按key值标识对应的事务工厂。
package df.middleware.mybatis.session
//环境
protected Environment environment;
// 类型别名注册机
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("DRUID", DruidDataSourceFactory.class);
}
public Environment getEnvironment() {
return environment;
}
public void setEnvironment(Environment environment) {
this.environment = environment;
}
BaseBuilder类:修改点如下,添加构造方法并以Configuration方式传入,这样子类XMLConfigBuilder的类可以直接使用此配置来处理或添加配置。
package df.middleware.mybatis.builder
protected final TypeAliasRegistry typeAliasRegistry;
public BaseBuilder(Configuration configuration){
this.configuration = configuration;
this.typeAliasRegistry = configuration.getTypeAliasRegistry();
}
XMLConfigBuilder类:添加数据库环境解析方法,来解析xml中的配置,获取dataSource配置信息,获取事务信息,并解析生成对应的事务工厂和数据源对象
并把上期加入的解析mapperElement方法更改Sql的结构部分所以也要把这个设置部分更改掉,把有关Sql方面的全部存储到BoundSql实体映射里,再通过MappedStatement包装起来
package df.middleware.mybatis.builder.xml
/**
* 解析配置:类型别名、插件、对象工厂、对象包装工厂、设置、环境、类型转换、映射器
*/
public Configuration parse() {
try {
// 环境解析
environmentsElement(root.element("environments"));
// 解析映射器
mapperElement(root.element("mappers"));
} catch (Exception e) {
e.printStackTrace();
}
return configuration;
}
// 解析数据源,事务等,并最终存储到环境类里
private void environmentsElement(Element context) throws Exception {
String enviroment = context.attributeValue("default");
List<Element> enviromentList = context.elements("environment");
for (Element e : enviromentList) {
String id = e.attributeValue("id");
if (enviroment.equals(id)) {
// 数据源
Element dataSourceElement = e.element("dataSource");
// 从类型注册机里找到DRUID名字的并得到类DataSourceFactory
DataSourceFactory dataSourceFactory = (DataSourceFactory) typeAliasRegistry.resolveAlias(dataSourceElement.attributeValue("type")).newInstance();
// 获取xml中数据源的属性数据
List<Element> propertiesList = dataSourceElement.elements("property");
Properties properties = new Properties();
for (Element prop : propertiesList) {
properties.setProperty(prop.attributeValue("name"), prop.attributeValue("value"));
}
// 设置数据源属性对象
dataSourceFactory.setProperties(properties);
// 设置DruidDataSource数据源并返回
DataSource dataSource = dataSourceFactory.getDataSource();
// 找到类型注册机里JDBC事务管理器
TransactionFactory txFactory = (TransactionFactory) typeAliasRegistry.resolveAlias(e.element("transactionManager").attributeValue("type")).newInstance();
// 构建环境-通过建造者模式
Environment.Builder environmentBuilder = new Environment.Builder(id).dataSource(dataSource)
.transactionFactory(txFactory);
// 将环境信息存储到配置里configuration供其他需要的地方使用
configuration.setEnvironment(environmentBuilder.Builder());
}
}
}
// 将mapper中的xml配置解析出来存储到对应的实体类中
private void mapperElement(Element mappers) throws Exception {
// ...省略,上一章节有
// 添加BoundSql并把有关sql解析的存储在这里
BoundSql boundSql = new BoundSql(sql, paramter, parameterType, resultType);
MappedStatement mappedStatement = new
MappedStatement.Builder(configuration, msId, sqlCommandType,
boundSql).build();
}
BoundSql类:此类存储的是xml里的sql,如parameterType还有resultType,以及将参数处理成?等等都存放到BoundSql实体类
<mapper namespace="df.middleware.mybatis.dao.IUserDao">
<select id="queryUserInfoById" parameterType="java.lang.Long" resultType="df.middleware.mybatis.po.User">
SELECT id, userId, userHead,userName
FROM user
where id = #{id}
</select>
</mapper>
package df.middleware.mybatis.mapping
/**
* @Author df
* @Date 2022/5/26 16:09
* @Version 1.0
* 绑定的SQL,是从SqlSource而来,将动态内容都处理完成得到的SQL语句字符串,其中包括?,还有绑定的参数
*/
public class BoundSql {
private String sql;
private String parameterType;
private String resultType;
private Map<Integer, String> parameterMappings;
public BoundSql(String sql,Map<Integer, String> parameterMappings,String parameterType,String resultType){
this.sql = sql;
this.parameterMappings = parameterMappings;
this.parameterType = parameterType;
this.resultType = resultType;
}
// set/get
}
MappedStatement类:这个和Environment的处理比较像,建造器模式,然后里边存储的主要是xml里的id还有namespace,以及对应的BoundSql类对象,这样Configuration类里就可以直接MappedStatement就可以获取其他相关的实体类信息。
<mapper namespace="df.middleware.mybatis.dao.IUserDao">
<select id="queryUserInfoById">
</select>
</mapper>
package df.middleware.mybatis.mapping
public class MappedStatement {
private Configuration configuration;
private String id;
private SqlCommandType sqlCommandType;
private BoundSql boundSql;
//public MappedStatement(){}
public static class Builder {
private MappedStatement mappedStatement = new MappedStatement();
public Builder(Configuration configuration, String id, SqlCommandType sqlCommandType,
BoundSql boundSql) {
mappedStatement.configuration = configuration;
mappedStatement.id = id;
mappedStatement.sqlCommandType = sqlCommandType;
mappedStatement.boundSql = boundSql;
}
public MappedStatement build() {
assert mappedStatement.configuration != null;
assert mappedStatement.id != null;
return mappedStatement;
}
}
public Configuration getConfiguration() {
return configuration;
}
public String getId() {
return id;
}
public SqlCommandType getSqlCommandType() {
return sqlCommandType;
}
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
public void setSqlCommandType(SqlCommandType sqlCommandType) {
this.sqlCommandType = sqlCommandType;
}
public BoundSql getBoundSql() {
return boundSql;
}
}
DefaultSqlSession类:DefaultSqlSession里添加对Environment环境的使用可以取出对应的数据源连接数据库,也可以通过MappedStatement得到Sql信息,参数和结果信息
这样的化就去连接数据库,拼接传参数执行sql语句得到结果通过反射去对应对象即可,暂时是写死的后期会更改
@Override
public <T> T selectOne(String statement, Object parameter) {
try {
MappedStatement mappedStatement = configuration.getMappedStatement(statement);
Environment environment = configuration.getEnvironment();
Connection connection = null;
connection = environment.getDataSource().getConnection();
BoundSql boundSql = mappedStatement.getBoundSql();
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());
preparedStatement.setLong(1, Long.parseLong(((Object[]) parameter)[0].toString()));
ResultSet resultSet = preparedStatement.executeQuery();
List<T> list = resultSet2Obj(resultSet, Class.forName(boundSql.getResultType()));
return list.get(0);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {
List<T> list = new ArrayList();
try {
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()) {
// 新建实例
T obj = (T) clazz.newInstance();
for (int i = 1; i <= columnCount; i++) {
Object value = resultSet.getObject(i);
String columnName = metaData.getColumnName(i);
String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);
// clazz.getMethod(setMethod, value.getClass());
// 参数传入实体的方法名称,和方法对应的参数class
Method method;
if (value instanceof Timestamp) {
method = clazz.getMethod(setMethod, Date.class);
} else {
method = clazz.getMethod(setMethod, value.getClass());
}
// 反射调用方法
method.invoke(obj, value);
}
list.add(obj);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
测试准备
在resources下添加mybatis-config-datasource.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis_demo?useUnicode=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/User_Mapper.xml"/>
</mappers>
</configuration>
在resources/mapper下添加User_Mapper.xml文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<mapper namespace="df.middleware.mybatis.dao.IUserDao">
<select id="queryUserInfoById" parameterType="java.lang.Long" resultType="df.middleware.mybatis.po.User">
SELECT id, userId, userHead,userName
FROM user
where id = #{id}
</select>
</mapper>
在单元测试类里添加如下内容
@Test
public void test_SqlSessionFactory() throws IOException {
// 1. 从SqlSessionFactory中获取SqlSession
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
// 2. 获取映射器对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
// 3. 测试验证
User user = userDao.queryUserInfoById(1L);
logger.info("测试结果:{}", JSON.toJSONString(user));
}
单元测试结果如下,得到数据库数据

465

被折叠的 条评论
为什么被折叠?



