客户端测试代码
首先新建一个Maven工程
sqlMapConfig 配置文件
在resource目录下新建 sqlMapConfig.xml
配置文件,用来存放数据库配置信息,存放mapper.xml的全路径
xml里的标签都是自定义的
<configuration>
<!-- 数据库配置信息 -->
<dataSource>
<property name="driverClass" value="con.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///mybatis"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</dataSource>
</configuration>
UserMapper.xml 配置文件
在resource目录下新建 UserMapper.xml
配置文件,用来存放sql配置信息
<mapper namespace="user">
<!-- sql的唯一标识: namespace.id来组成 statementID -->
<select id="selectList" resultType="pojo.User">
select * from user
</select>
<select id="selectOne" resultType="pojo.User" paramType="pojo.User">
select * from user where id = #{id}
</select>
</mapper>
namespace
用来区分不同模块的mapper文件
select.id
用来区分同个模块下的不同sql语句
select
标签指定sql执行的方式
resultType
指定查询返回的实体类
paramterType
指定查询的参数类型
在sqlMapConfig.xml文件添加mapper.xml的路径
<configuration>
<!-- 数据库配置信息 -->
<dataSource>
<property name="driverClass" value="con.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///mybatis"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</dataSource>
<!-- 存放mapper.xml的全路径 -->
<mapper resource="UserMapper.xml"></mapper>
</configuration>
至此,客户端代码暂告一段落,接下来编写自定义持久层框架
自定义持久层框架
新建一个maven工程IPersistence
首先,我们要读取客户端的配置文件,加载成字节输入流
- 新建io包,创建Resource类:
package com.batic.io; import java.io.InputStream; public class Resource { /** * 根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中 * @param path 文件路径 * @return 流 */ public static InputStream getResourceAsSteam(String path){ return Resource.class.getClassLoader().getResourceAsStream(path); } }
- 创建两个Java Bean:存放对配置文件解析出来的内容
- Configuration:核心配置类:存放sqlMapConfig.xml 解析出来的内容
package com.batic.pojo; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; public class Configuration { private DataSource dataSource; /** * key: statementID * value: 封装好的mappedStatement对象 */ Map<String,MappedStatement> mappedStatementMap = new HashMap<String, MappedStatement>(); public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public Map<String, MappedStatement> getMappedStatementMap() { return mappedStatementMap; } public void setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) { this.mappedStatementMap = mappedStatementMap; } }
- MapedStatement :映射配置类:存放mapper.xml 解析出来的内容
package com.batic.pojo; public class MappedStatement { /** * id 标识 */ private String id; /** * 返回值类型 */ private String resultType; /** * 参数值类型 */ private String paramType; /** * sql语句 */ private String sql; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getResultType() { return resultType; } public void setResultType(String resultType) { this.resultType = resultType; } public String getParamType() { return paramType; } public void setParamType(String paramType) { this.paramType = paramType; } public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } }
- 创建两个Java Bean:存放对配置文件解析出来的内容
导入依赖
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.17</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
创建类 : SqlSessionFactoryBuilder ,方法:build(InputSteam in)
作用:解析配置文件,生产SqlSession
package com.batic.sqlSession;
import com.batic.config.XMLConfigBuilder;
import com.batic.pojo.Configuration;
import org.dom4j.DocumentException;
import java.beans.PropertyVetoException;
import java.io.InputStream;
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream in) throws DocumentException, PropertyVetoException {
// 1.使用dom4j解析配置文件,将解析出来的内容封装到Configuration中
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
Configuration configuration = xmlConfigBuilder.parseConfig(in);
// 2.创建sqlSessionFactory对象:工厂类:生产SqlSession:会话对象
DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration);
return defaultSqlSessionFactory;
}
}
创建SqlSessionFactory接口和实现类DefaultSqlSessionFactory
SqlSessionFactory接口
package com.batic.sqlSession;
public interface SqlSessionFactory {
public SqlSession openSession();
}
DefaultSqlSessionFactory实现类
package com.batic.sqlSession;
import com.batic.pojo.Configuration;
/**
* SqlSession工厂类:生产SqlSession:会话对象
* @author Administrator
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory{
private Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
return new DefaultSqlSession(configuration);
}
}
创建SqlSession接口及实现类DefaultSession
定义对数据库的crud操作
SqlSession接口:
package com.batic.sqlSession;
public interface SqlSessionFactory {
public SqlSession openSession();
}
SqlSession接口实现类:
package com.batic.sqlSession;
import java.util.List;
public interface SqlSession {
/**
* 查询所有
*/
public <E> List<E> selectList(String statementId, Object...params) throws Exception;
/**
* 根据条件查询单个
*/
public <T> T selectOne(String statementId, Object...params) throws Exception;
}
创建Executor接口及实现类SimpleExcutor实现类
封装JDBC,执行JDBC代码
package com.batic.sqlSession;
import com.batic.pojo.Configuration;
import com.batic.pojo.MappedStatement;
import java.util.List;
public interface Executor {
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object...params) throws Exception;
}
最关键的步骤
package com.batic.sqlSession;
import com.batic.config.BoundSql;
import com.batic.pojo.Configuration;
import com.batic.pojo.MappedStatement;
import com.batic.utils.GenericTokenParser;
import com.batic.utils.ParameterMapping;
import com.batic.utils.ParameterMappingTokenHandler;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class SimpleExecutor implements Executor{
@Override
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception{
// 注册驱动,获取数据库连接
Connection connection = configuration.getDataSource().getConnection();
// 获取sql语句 select * from use where id = #{id} and username = #{username}
String sql = mappedStatement.getSql();
// 转换sql语句 select * from use where id = ? and username = ? 转换的过程中,还需要对#{}立面的值进行解析存储
BoundSql boundSql = getBoundSql(sql);
// 获取预处理对象:preparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
// 设置参数
// 获取参数的全路径
String paramType = mappedStatement.getParamType();
// 获取参数的Class类
Class<?> paramterTypeClass = getClassType(paramType);
List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();
for (int i = 0; i < parameterMappingList.size(); i++) {
ParameterMapping parameterMapping = parameterMappingList.get(i);
String content = parameterMapping.getContent();
// 使用反射获取实体类的对应属性值
Field declaredField = paramterTypeClass.getDeclaredField(content);
// 暴力访问
declaredField.setAccessible(true);
// 属性的类型
Object o = declaredField.get(params[0]);
preparedStatement.setObject(i+1,o);
}
// 执行sql
ResultSet resultSet = preparedStatement.executeQuery();
String resultType = mappedStatement.getResultType();
Class<?> resultTypeClass = getClassType(resultType);
ArrayList<Object> objects = new ArrayList<>();
// 封装返回结果集
while (resultSet.next()){
Object o = resultTypeClass.newInstance();
// 元数据
ResultSetMetaData metaData = resultSet.getMetaData();
for (int i = 1; i <= metaData.getColumnCount(); i++) {
// 获取字段名
String columnName = metaData.getColumnName(i);
// 获取字段值
Object object = resultSet.getObject(columnName);
// 使用反射或者内省,根据数据库表和实体的对应关系,完成封装
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(o,object);
}
objects.add(o);
}
return (List<E>) objects;
}
/**
* 根据类的全路径 获取类的Class对象
* @return
*/
private Class<?> getClassType(String paramType) throws ClassNotFoundException {
if(paramType != null) {
return Class.forName(paramType);
}
return null;
}
/**
* 完成对#{}的解析工作:
* 1.将#{}使用?代替
* 2.解析出#{}里面的值进行存储
* @param sql
* @return
*/
private BoundSql getBoundSql(String sql) {
// 标记处理类:配置标记解析器来完成对占位符的解析处理工作
ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
// 标记解析器:
GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
// 解析后的sql
String parseSql = genericTokenParser.parse(sql);
// #{} 里面解析出来的参数名称
List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
return new BoundSql(parseSql,parameterMappings);
}
}
这里有一些解析xml的方法直接借用了mybatis官方的方法,就不贴上来,我直接上传到GitHub
GitHub地址
客户端测试
导入依赖
<!-- 引入自定义持久层框架的依赖 -->
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>IPersistence</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
package test;
import com.batic.io.Resource;
import com.batic.sqlSession.SqlSession;
import com.batic.sqlSession.SqlSessionFactory;
import com.batic.sqlSession.SqlSessionFactoryBuilder;
import org.junit.Test;
import pojo.User;
import java.io.InputStream;
import java.util.List;
public class IPersistenceTest {
@Test
public void test() throws Exception {
InputStream inputStream = Resource.getResourceAsSteam("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> userList = sqlSession.selectList("user.selectList");
for (User user : userList) {
System.out.println(user);
}
User user1 = new User();
user1.setId(2);
User data = (User)sqlSession.selectOne("user.selectOne",user1);
System.out.println(data);
}
}
输出
User{id=1, username=‘张三’}
User{id=2, username=‘jack’}
User{id=3, username=‘kk’}
User{id=2, username=‘jack’}