mybatis
JDBC
JDBC存 的问题
-
数据库配置硬编码
-
频繁创建与销毁数据库连接 ,资源严重浪费
-
SQL语句、参数写在代码里和代码在一起,存在硬编码,不好维护,
-
对参数和返回结果需要手动一个一个去绑定,较为繁琐
JDBC 解决方案
-
使用配置文件
-
使用数据库连接池
-
使用配置文件与代码分开维护(与数据库配置文件分开),SQL配置文件可能经常变更
-
使用反射、内省的方式来重实现绑定与封装
自定义持久层-persistent
使用端引用自定义持久框架 的jar包
提供数据库配置信息-配置文件
sqlMapConfig.xml存放数据库配置信息
sql配置信息-sql语句,入参数 ,返回类型-配置文件
UserMapper.xml-存放Sql的配置信息
自定义持久层框架本身
本质是对JDBC的封装
解析sqlMapConfig.xml、UserMapper.xml配置文件
- 加载配置文件-加载为流
- 将解析到XML内容存放在JAVABENA(容器) Configuration核心配置类sqlMapConfig.xm,MappedStatement映射配置类
- 解析XML-使用DOM4J,将数据封装
- 创建SqlSessionFactoryBuilder与SqlSessionFactory
- 创建SqlSession会话对象-增删改查
- 创建Executor ,执行JDBC,
使用
<!--sqlMapConfig.xml-->
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<properties>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/mylearning"/>
<property name="jdbcUser" value="root"/>
<property name="jdbcPassword" value="root"/>
</properties>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
<!--UserMapper-->
<mapper namespace="com.liu.me.mapper.UserMapper">
<select id="selectOne" paramterType="com.liu.me.entity.User"
resultType="com.liu.me.entity.User">
select * from user where id = #{id} and username =#{username}
</select>
<select id="selectList" resultType="com.liu.me.entity.User">
select * from user
</select>
</mapper>
持久层框架的基本实现
<dependencies>
<!--mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!--dom4j-->
<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>
<!--数据库连接池-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
</dependencies>
加载资源类
public class Resources {
/**
* 根据配置文件的路径加载资源
* @param path
* @return
*/
public static InputStream getResourceAsStream(String path){
return Resources.class.getClassLoader().getResourceAsStream(path);
}
}
SqlCommandType
public enum SqlCommandType {
INSERT, UPDATE, DELETE, SELECT;
}
配置类 MappedStatement
public class MappedStatement {
/**
* ID namespace +"."+id
*/
private String id;
/**
* SQL语句
*/
private String sql;
private String parameterType;
private String returnType;
private SqlCommandType sqlCommandType;
}
配置类 Configuration
public class Configuration {
private DataSource dataSource;
private Properties properties;
private Map<String, MappedStatement> mappedStatementMap = new ConcurrentHashMap<>();;
}
xml解析-SqlSessionFactoryBuilder
public class SqlSessionFactoryBuilder {
/**
* 创建SqlSessionFactory
* @param in
* @return
*/
public SqlSessionFactory build(InputStream in) throws DocumentException, PropertyVetoException {
XmlConfigBuilder xmlConfigBuilder = new XmlConfigBuilder();
Configuration configuration = xmlConfigBuilder.parseConfig(in);
//创建SqlSessionFactory
return null;
}
}
xml解析-XmlConfigBuilder
public class XmlConfigBuilder {
private Configuration configuration;
public XmlConfigBuilder() {
this.configuration = new Configuration();
}
/**
* 解析配置文件封装Configuration对象
* @param inputStream
* @return
*/
public Configuration parseConfig(InputStream inputStream) throws DocumentException, PropertyVetoException {
//使用Dom4j
Document document = new SAXReader().read(inputStream);
//获取根据节点
Element rootElement = document.getRootElement();
//property 获取所有
List<Element> list =rootElement.selectNodes("//property");
Properties properties = new Properties();
for (Element element : list) {
//获取name
String name = element.attributeValue("name");
//获取value
String value = element.attributeValue("value");
properties.put(name, value);
}
configuration.setProperties(properties);
//创建数据源
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//driverClass
dataSource.setDriverClass(properties.getProperty("driverClass"));
//jdbcUrl
dataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
//jdbcUser
dataSource.setUser(properties.getProperty("jdbcUser"));
//jdbcPassword
dataSource.setPassword(properties.getProperty("jdbcPassword"));
//设置到配置类中
configuration.setDataSource(dataSource);
List<Element> mappers =rootElement.selectNodes("//mapper");
for (Element element : mappers) {
String resource = element.attributeValue("resource");
InputStream asStream = Resources.getResourceAsStream(resource);
XmlMappedBuilder xmlMappedBuilder = new XmlMappedBuilder(configuration);
xmlMappedBuilder.parse(inputStream);
}
return configuration;
}
}
xml解析-XmlMappedBuilder
public class XmlMappedBuilder {
private Configuration configuration;
public XmlMappedBuilder(Configuration configuration) {
this.configuration = configuration;
}
public void parse(InputStream inputStream) throws DocumentException {
//使用Dom4j
Document document = new SAXReader().read(inputStream);
//获取根对象
Element rootElement = document.getRootElement();
String namespace = rootElement.attributeValue("namespace");
List<Element> selectList=rootElement.selectNodes("//select");
doConfig(namespace,selectList,SqlCommandType.SELECT);
List<Element> updateList= rootElement.selectNodes("//update");
doConfig(namespace,selectList,SqlCommandType.UPDATE);
List<Element> deleteList= rootElement.selectNodes("//delete");
doConfig(namespace,selectList,SqlCommandType.DELETE);
List<Element> insertList= rootElement.selectNodes("//insert");
doConfig(namespace,selectList,SqlCommandType.INSERT);
}
private void doConfig(String namespace,List<Element> list, SqlCommandType sqlCommandType) {
for (Element element : list) {
//id
String id = element.attributeValue("id");
//参数
String paramterType = element.attributeValue("paramterType");
//返回值
String resultType = element.attributeValue("resultType");
//sql
String sqlText = element.getTextTrim();
//封装数据
MappedStatement mappedStatement = new MappedStatement();
mappedStatement.setSqlCommandType(sqlCommandType);
//id值命名空间.id
String key =namespace+"."+id;
mappedStatement.setId(key);
mappedStatement.setSql(sqlText);
mappedStatement.setReturnType(resultType);
mappedStatement.setParameterType(paramterType);
//放入容器
configuration.getMappedStatementMap().put(key, mappedStatement);
}
}
}
到此XML解析完毕
创建SqlSessionFactory
public class SqlSessionFactoryBuilder {
/**
* 创建SqlSessionFactory
* @param in
* @return
*/
public SqlSessionFactory build(InputStream in) throws DocumentException, PropertyVetoException {
XmlConfigBuilder xmlConfigBuilder = new XmlConfigBuilder();
Configuration configuration = xmlConfigBuilder.parseConfig(in);
//创建SqlSessionFactory
return new DefaultSqlSessionFactory(configuration);
}
}
SqlSessionFactory
public interface SqlSessionFactory {
SqlSession openSqlSession();
}
DefaultSqlSessionFactory
public class DefaultSqlSessionFactory implements SqlSessionFactory{
private Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSqlSession() {
//传配置类
return new DefaultSqlSession(configuration);
}
}
SqlSession
public interface SqlSession {
/**
*
* @param statementId
* @param params
* @param <E>
* @return
*/
<E> List<E> selectList(String statementId,Object...params);
/**
*
* @param statementId
* @param params
* @return
*/
<T> T selectOne(String statementId,Object...params);
}
DefaultSqlSession
public class DefaultSqlSession implements SqlSession {
/**
* 醘
*/
private Configuration configuration;
/**
* 执行器
*/
private Executor executor;
public DefaultSqlSession(Configuration configuration) {
this.configuration = configuration;
executor=new SimpleExecutor();
}
@Override
public <E> List<E> selectList(String statementId, Object... params) {
executor = new SimpleExecutor();
MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
return executor.query(configuration, mappedStatement, params);
}
@Override
public <T> T selectOne(String statementId, Object... params) {
List<T> list = selectList(statementId, params);
if(list.size()==1){
return list.get(0);
}
throw new RuntimeException("返回结果过多");
}
}
Executor
public interface Executor {
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement,Object[] params);
void close() throws SQLException;
}
SimpleExecutor
package com.liu.me.executor;
import com.liu.me.mapping.BoundSql;
import com.liu.me.mapping.MappedStatement;
import com.liu.me.session.Configuration;
import com.liu.me.util.GenericTokenParser;
import com.liu.me.util.ParameterMapping;
import com.liu.me.util.ParameterMappingTokenHandler;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* @Description
* @ClassName SimpleExecutor
* @Author 刘楠
* @date 2020.06.19
*/
public class SimpleExecutor implements Executor {
private Connection connection;
@Override
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws SQLException, NoSuchFieldException, IllegalAccessException, IntrospectionException, InvocationTargetException, InstantiationException {
//1.创建连接
connection = configuration.getDataSource().getConnection();
//2. 获取sql语句 : select * from user where id = #{id} and username = #{username}
// //转换sql语句: select * from user where id = ? and username = ? ,转换的过程中,还需要对#{}里面的值进行解析存储
String sql = mappedStatement.getSql();
BoundSql boundSql = getBoundSql(sql);
//3. 获取预处理对象:preparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
//4. 设置参数
String parameterType = mappedStatement.getParameterType();
Class<?> paramtertypeClass = null;
if (null != parameterType) {
paramtertypeClass = getClassType(parameterType);
}
if (paramtertypeClass != null) {
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
for (int i = 0; i < parameterMappings.size(); i++) {
//获取元素
ParameterMapping mapping = parameterMappings.get(i);
//属性名
String content = mapping.getContent();
//反射获取属性
Field field = paramtertypeClass.getDeclaredField(content);
//设置可访问
field.setAccessible(true);
Object o = field.get(params[0]);
//为SQL中的?设置值
preparedStatement.setObject(i + 1, o);
}
}
//5. 执行sql
ResultSet resultSet = preparedStatement.executeQuery();
//6.结果封装
String returnType = mappedStatement.getReturnType();
Class<?> resultTypeClass = getClassType(returnType);
List<Object> objects = new ArrayList<>();
while (resultSet.next()) {
Object newInstance = resultTypeClass.newInstance();
//元数据
ResultSetMetaData metaData = resultSet.getMetaData();
//获取列组
int columnCount = metaData.getColumnCount();
//从1开始
for (int i = 1; i <=columnCount; i++) {
//获取名
String columnName = metaData.getColumnName(i);
//获取查询出的出来值
Object value = resultSet.getObject(columnName);
//使用反射java.beans中的内省
PropertyDescriptor descriptor = new PropertyDescriptor(columnName, resultTypeClass);
//获取写方法
Method writeMethod = descriptor.getWriteMethod();
writeMethod.invoke(newInstance, value);
}
objects.add(newInstance);
}
return (List<E>)objects;
}
@Override
public void close() throws SQLException {
if (connection != null) {
connection.close();
}
}
private Class<?> getClassType(String paramterType) {
if (paramterType != null) {
Class<?> aClass = null;
try {
aClass = Class.forName(paramterType);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return aClass;
}
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();
BoundSql boundSql = new BoundSql(parseSql, parameterMappings);
return boundSql;
}
}
测试
public class User {
private Integer id;
private String username;
private String password;
private Date birthday;
}
public static void main(String[] args) throws PropertyVetoException, DocumentException, IllegalAccessException, IntrospectionException, InstantiationException, SQLException, InvocationTargetException, NoSuchFieldException {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
System.err.println(inputStream);
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sessionFactory = factoryBuilder.build(inputStream);
User user = new User();
user.setId(1);
user.setUsername("lucy");
User userResult = sessionFactory.openSqlSession().selectOne("com.liu.me.mapper.UserMapper.selectOne", user);
System.err.println(userResult);
List<User> userList = sessionFactory.openSqlSession().selectList("com.liu.me.mapper.UserMapper.selectList", null);
System.err.println(userList);
}
SqlSession 中增加getMapper
public interface SqlSession {
/**
*
* @param statementId
* @param params
* @param <E>
* @return
*/
<E> List<E> selectList(String statementId,Object...params) throws SQLException, InvocationTargetException, IntrospectionException, InstantiationException, IllegalAccessException, NoSuchFieldException;
/**
*
* @param statementId
* @param params
* @return
*/
<T> T selectOne(String statementId,Object...params) throws SQLException, IntrospectionException, InstantiationException, IllegalAccessException, NoSuchFieldException, InvocationTargetException;
<T> T getMapper(Class<T> mapperClass);
}
DefaultSqlSession 中增加getMapper
@Override
public <T> T getMapper(Class<T> mapperClass) {
T t =(T) Proxy.newProxyInstance(mapperClass.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取方法名
String methodName = method.getName();
//获取namespace
String className = method.getDeclaringClass().getName();
String statementId = className+"."+methodName;
//判断是否有泛型
Type type = method.getGenericReturnType();
if(type instanceof ParameterizedType){
return selectList(statementId, args);
}
return selectOne(statementId, args);
}
});
return t;
}