简易框架功能介绍
搭建这个简易的框架是为了加深对mybatis的理解,功能不是全部实现的(也没有能力),所以这个简易的框架的功能只支持表字段都为varchar,pojo为String类型的,而且本框架只支持JDBC事务管理器,只支持非池化,功能的话只实现了插入,查询(单个数据不支持多个)。
创建模块导入相关pom依赖
使用dom4j去进行解析xml文件
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>mybatis-xml-dom4j</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--dom4j依赖-->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<!--jaxen依赖-->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.2.0</version>
</dependency>
<!--junit依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>
资源工具类
这个类用于读取指定配置文件的输入流,即读取resourse目录下的文件
代码语言:javascript复制/**
* @author 风轻云淡
*/
public class Resource {
/**
* 从类路径中获取配置文件输入输出流动
*/
public static InputStream getResourcesAsStream(String path){
return Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
}
}
sqlSessionFactoryBuilder工厂构造类
读取batis核心配置文件,工具SqlSessionFactory对象
代码语言:javascript复制build方法主要负责功能解析配置文件: 创建数据源对象 创建事物管理器对象 获取所有的SQL映射文件
/**
* @author 风轻云淡
*/
public class SqlSessionFactoryBuilder {
public SqlSessionFactoryBuilder() {
}
/**
* 获取SqlSessionFactory对象
* 读取batis核心配置文件,工具SqlSessionFactory对象
* @param inputStream 指向核心配置文件的输入流动
* @return
*/
public SqlSessionFactory build(InputStream inputStream){
/**
* 主要负责功能解析配置文件
* 创建数据源对象
* 创建事物管理器对象
* 获取所有的SQL映射文件
* 封装到SqlSessionFactory对象中
*
*/
return null;
}
}
SqlSessionFactory设计和MappedStatement的编写
这个类应该包含: 一个属性为事务管理器,对应执行sql语句的MappedStatement对象 JDBCTransaction属性 Map<String, MappedStatement>属性
MappedStatement:
代码语言:javascript复制/**
* @author 风轻云淡
*/
public class MapperStatement {
private String sqlId;
private String resultType;
private String sql;
private String parameterType;
private String sqlType;
public MapperStatement(String sqlId, String resultType, String sql, String parameterType, String sqlType) {
this.sqlId = sqlId;
this.resultType = resultType;
this.sql = sql;
this.parameterType = parameterType;
this.sqlType = sqlType;
}
@Override
public String toString() {
return "MapperStatement{"
"sqlId='" sqlId '''
", resultType='" resultType '''
", sql='" sql '''
", parameterType='" parameterType '''
", sqlType='" sqlType '''
'}';
}
public String getSqlId() {
return sqlId;
}
public void setSqlId(String sqlId) {
this.sqlId = sqlId;
}
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public String getParameterType() {
return parameterType;
}
public void setParameterType(String parameterType) {
this.parameterType = parameterType;
}
public String getSqlType() {
return sqlType;
}
public void setSqlType(String sqlType) {
this.sqlType = sqlType;
}
}
JDBCTransaction设计
我们知道mybatis的事务管理器类型有只能填MANAGED或者JDBC,在本框架中我们只实现最简单的JDBC事务管理器
● transactionManager:配置事务管理器 ○ type属性:指定事务管理器具体使用什么方式,可选值包括两个 ■ JDBC:使用JDBC原生的事务管理机制。底层原理:事务开启conn.setAutoCommit(false); ...处理业务...事务提交conn.commit(); ■ MANAGED:交给其它容器来管理事务,比如WebLogic、JBOSS等。如果没有管理事务的容器,则没有事务。没有事务的含义:只要执行一条DML语句,则提交一次。
编辑
事务管理器最好是定义一个接口,然后每一个具体的事务管理器都实现这个接口。
代码语言:javascript复制/**
* @author 风轻云淡
*/
public class JDBCTransaction implements TransactionManager{
/**
* 数据库连接对象
*/
private Connection connection;
/**
* 数据源对象
*/
private DataSource dataSource;
/**
* 自动提交标记
* true:自动提交
* false:不自动提交
*/
private boolean autoCommit;
/**
* 构造事务管理器对象
*/
public JDBCTransaction( DataSource dataSource,Connection connection) {
this.connection = connection;
this.dataSource = dataSource;
}
@Override
public void commit() {
try {
connection.commit();
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void rollback() {
try {
connection.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Override
public void close() {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Override
public void openConnection() {
try {
Connection connection = dataSource.getConnection();
this.connection.setAutoCommit(this.autoCommit);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Override
public Connection getConnection() {
return connection;
}
}
JDBC事务管理器实现类
代码语言:javascript复制/**
* @author 风轻云淡
*/
public class JDBCTransaction implements TransactionManager{
@Override
public void commit() {
}
@Override
public void rollback() {
}
@Override
public void close() {
}
@Override
public void openConnection() {
}
@Override
public Connection getConnection() {
return null;
}
}
UNPOOLEDDataSource数据源类设计
代码语言:javascript复制unpool即在本框架中不使用数据库连接池技术实现
/**
* @author 风轻云淡
*/
public class UNPOOLEDDataSource implements javax.sql.DataSource{
private String url;
private String username;
private String password;
public UNPOOLEDDataSource(String driver,String url,String username,String password){
try {
Class.forName(driver);
}catch (Exception e){
e.printStackTrace();
}
this.url=url;
this.username=username;
this.password=password;
}
@Override
public Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
SqlSessionFactory类完善
代码语言:javascript复制/**
* @author 风轻云淡
*/
public class SqlSessionFactory {
private TransactionManager transactionManager;
private Map<String,MapperStatement> mapperStatements;
public SqlSessionFactory(TransactionManager transactionManager, Map<String, MapperStatement> mapperStatements) {
this.transactionManager = transactionManager;
this.mapperStatements = mapperStatements;
}
public TransactionManager getTransactionManager() {
return transactionManager;
}
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public Map<String, MapperStatement> getMapperStatements() {
return mapperStatements;
}
public void setMapperStatements(Map<String, MapperStatement> mapperStatements) {
this.mapperStatements = mapperStatements;
}
}
SqlSessionFactoryBuilder中的build方法编写
代码语言:javascript复制/**
* @author 风轻云淡
*/
public class SqlSessionFactoryBuilder {
public SqlSessionFactoryBuilder() {
}
/**
* 获取SqlSessionFactory对象
* 读取batis核心配置文件,工具SqlSessionFactory对象
* @param inputStream 指向核心配置文件的输入流动
* @return
*/
public SqlSessionFactory build(InputStream inputStream) throws DocumentException {
/**
* 主要负责功能解析配置文件
* 创建数据源对象
* 创建事物管理器对象
* 获取所有的SQL映射文件
* 封装到SqlSessionFactory对象中
*
*/
Document document = new SAXReader().read(inputStream);
Element environmentsElt = (Element) document.selectSingleNode("/configuration/environments");
String defaultEnv = environmentsElt.attributeValue("default");
//解析文件得出数据源对象
Element dataSourceElt = environmentsElt.element("dataSource");
DataSource dataSource= getDataSource(dataSourceElt);
//解析文件得出事务管理器对象
Element transactionManagerElt = environmentsElt.element("transactionManager");
TransactionManager transactionManager =gettransactionManager(transactionManagerElt,dataSource);
//解析文件得出sql映射对象
Element mapperElt=environmentsElt.element("mappers");
Map<String,MapperStatement> mapperStatementMap =getMapperStatements(mapperElt);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(transactionManager, mapperStatementMap);
return sqlSessionFactory;
}
private Map<String, MapperStatement> getMapperStatements(Element mapperElt) {
Map<String,MapperStatement> mapperStatements=new HashMap<>();
try {
String resource = mapperElt.attributeValue("resource");
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(Resource.getResourcesAsStream(resource));
Element mapper = (Element) document.selectSingleNode("/mapper");
String namespace = mapper.attributeValue("namespace");
mapper.elements().forEach(item->{
String sqlId = item.attributeValue("id");
String sql = item.getTextTrim();
String parameterType = item.attributeValue("parameterType");
String resultType = item.attributeValue("resultType");
String sqlType = item.getName().toLowerCase();
//封装对象
MapperStatement mapperStatement = new MapperStatement(sqlId, resultType, sql, parameterType, sqlType);
mapperStatements.put(namespace "." sqlId,mapperStatement);
});
}catch (Exception e){
e.printStackTrace();
}
return mapperStatements;
}
private TransactionManager gettransactionManager(Element transactionManagerElt, DataSource dataSource) {
String type = transactionManagerElt.attributeValue("type").toUpperCase();
TransactionManager transactionManager=null;
if("JDBC".equals(type)){
transactionManager=new JDBCTransaction(dataSource,false);
}else{
try {
throw new Exception("本框架只支持JDBC事务管理");
} catch (Exception e) {
e.printStackTrace();
}
};
return transactionManager;
}
private DataSource getDataSource(Element dataSourceElt) {
//获取所有数据源配置
Map<String ,String> dataSourceMap=new HashMap<>();
dataSourceElt.elements().forEach(item->{
dataSourceMap.put(item.attributeValue("name"),item.attributeValue("value"));
});
String dataSourceType = dataSourceElt.attributeValue("type").toUpperCase();
DataSource dataSource=null;
if("UNPOOLED".equals(dataSourceType)){
dataSource=new UNPOOLEDDataSource(dataSourceMap.get("driver"),
dataSourceMap.get("url"),
dataSourceMap.get("username"),
dataSourceMap.get("password"));
}else{
try {
throw new Exception("本框架只实现了UNPOOLED");
} catch (Exception e) {
e.printStackTrace();
}
}
return dataSource;
}
}
SqlSession编写
代码语言:javascript复制public class SqlSession {
TransactionManager transactionManager;
Map<String, MapperStatement> mapperStatements;
public SqlSession(TransactionManager transactionManager, Map<String, MapperStatement> mapperStatements) {
this.transactionManager = transactionManager;
this.mapperStatements = mapperStatements;
}
public void commit(){
try {
transactionManager.getConnection().commit();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void rollback(){
try {
transactionManager.getConnection().rollback();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void close(){
try {
transactionManager.getConnection().close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
在SqlSessionFactory中添加openSession方法
代码语言:javascript复制public SqlSession openSession(){
transactionManager.openConnection();
SqlSession sqlSession = new SqlSession(transactionManager, mappedStatements);
return sqlSession;
}
编写SqlSession类中的insert方法
代码语言:javascript复制 /**
* 插入数据
*
* @param sqlId 要执行的sqlId
* @param obj 插入的数据
* @return
*/
public int insert(String sqlId, Object obj) {
MapperStatement godMappedStatement = mapperStatements.get(sqlId);
Connection connection = transactionManager.getConnection();
// 获取sql语句
// insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
String godbatisSql = godMappedStatement.getSql();
// insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,?,?,?,?,?)
String sql = godbatisSql.replaceAll("#\{[a-zA-Z0-9_\$]*}", "?");
// 重点一步
Map<Integer, String> map = new HashMap<>();
int index = 1;
while (godbatisSql.indexOf("#") >= 0) {
int beginIndex = godbatisSql.indexOf("#") 2;
int endIndex = godbatisSql.indexOf("}");
map.put(index , godbatisSql.substring(beginIndex, endIndex).trim());
godbatisSql = godbatisSql.substring(endIndex 1);
}
final PreparedStatement ps;
try {
ps = connection.prepareStatement(sql);
// 给?赋值
map.forEach((k, v) -> {
try {
// 获取java实体类的get方法名
String getMethodName = "get" v.toUpperCase().charAt(0) v.substring(1);
Method getMethod = obj.getClass().getDeclaredMethod(getMethodName);
ps.setString(k, getMethod.invoke(obj).toString());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
int count = ps.executeUpdate();
ps.close();
return count;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
编写SqlSession类中的selectOne方法
代码语言:javascript复制 /**
* 查询一个对象
* @param sqlId
* @param parameterObj
* @return
*/
public Object selectOne(String sqlId, Object parameterObj){
MapperStatement godMappedStatement = mapperStatements.get(sqlId);
Connection connection = transactionManager.getConnection();
// 获取sql语句
String godbatisSql = godMappedStatement.getSql();
String sql = godbatisSql.replaceAll("#\{[a-zA-Z0-9_\$]*}", "?");
// 执行sql
PreparedStatement ps = null;
ResultSet rs = null;
Object obj = null;
try {
ps = connection.prepareStatement(sql);
ps.setString(1, parameterObj.toString());
rs = ps.executeQuery();
if (rs.next()) {
// 将结果集封装对象,通过反射
String resultType = godMappedStatement.getResultType();
Class<?> aClass = Class.forName(resultType);
Constructor<?> con = aClass.getDeclaredConstructor();
obj = con.newInstance();
// 给对象obj属性赋值
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
for (int i = 1; i <= columnCount; i ) {
String columnName = rsmd.getColumnName(i);
String setMethodName = "set" columnName.toUpperCase().charAt(0) columnName.substring(1);
Method setMethod = aClass.getDeclaredMethod(setMethodName, aClass.getDeclaredField(columnName).getType());
setMethod.invoke(obj, rs.getString(columnName));
}
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
try {
ps.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
return obj;
}
我正在参与2023腾讯技术创作特训营第二期有奖征文,瓜分万元奖池和键盘手表