一、为啥自造
1. 学习框架设计的思想
2. 深入了解Mybatis核心技术
3. 加强低层技术的应用
二、解决问题
1、解决sql硬编码问题
使用xml封装数据库连接信息, 和sql执行语句
2、解决资源浪费问题
封装c3p0数据库连接池技术
3、解决结果集处理代码重复问题
使用反射技术封装成java对象返回
三、目标技术
1. 加载主配置文件
dom4j 解析XML
2. 创建构建器
构建器模式: 根据要求构建出相应的对象
3. 构建会话工厂
工厂模式: 将实例化对象的工作交给工厂类
4. 打开数据库连接会话
mysql: 数据库连接
c3p0: 连接池技术5. 获取用户操作映射器代理对象
动态代理: 动态对目标对象的方法进行增强处理
泛型: 动态(待确认)类型占位符6. 反射技术
7. 处理结果
8. 关闭资源
9. 启动程序
Junit: 单元测试
四、实现步骤
核心配置的加载
1. Configuration
创建Configuration类, 封装 mybatis.xml 配置内容
2. Mapper
创建Mapper类, 封装 userMapper.xml 配置内容
核心代码封装
1. SqlSession
提供getMapper()方法获取代理对象
2. MapperProxyFactory
创建UserDao接口的 代理对象
操作工具封装
1. Executor
编写执行数据库CRUD的操作工具
五、编码实现
1、创建自定义模块
2、依赖引入
版本控制:
<properties>
<dom4j.version>1.6.1</dom4j.version>
<jaxen.version>1.1.6</jaxen.version>
<!-- mysql依赖 -->
<mysql.version>8.0.15</mysql.version>
<!-- c3p0连接池依赖 -->
<c3p0.version>0.9.1.2</c3p0.version>
<!-- Junit单元测试依赖 -->
<junit.version>4.12</junit.version>
</properties>
dom4j
<!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>${dom4j.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/jaxen/jaxen -->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>${jaxen.version}</version>
</dependency>
mysql
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
c3p0
<!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
junit
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
创建Configuration类, 封装 mybatis.xml 配置内容
import com.itheima.mybatis.day01.custom.kit.Resources;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.beans.PropertyVetoException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 封装主配置内容.
*
* @author Jason
*/
public class Configuration {
// 数据库连接信息
private String driver;
private String url;
private String name;
private String pass;
// ① 映射器集合
// key: namespace.id;
// val: mapper
private Map<String, Mapper> mappers = new HashMap();
// ② 连接池数据源
private ComboPooledDataSource datasource;
public Configuration() {
// 1. 映射器集合
loadMybatisXml();
// 2. 连接池数据源
createDatasource();
}
/**
* 加载主配置文件
* 知识点:
* dom4j
*/
private void loadMybatisXml() {
try {
InputStream in =
Resources.getResourceAsStream("mybatis.xml");
SAXReader reader = new SAXReader();
// 获取数据库配置信息
List<Element> properties = root.selectNodes("//property");
for (Element prop : properties) {
String name = prop.attributeValue("name");
String value = prop.attributeValue("value");
if ("driver".equals(name)) {
this.driver = value;
}
if ("url".equals(name)) {
this.url = value;
}
if ("username".equals(name)) {
this.name = value;
}
if ("password".equals(name)) {
this.pass = value;
}
}
// 获取映射器配置文件
List<Element> mappers = root.selectNodes("//mapper");
for (Element mapper : mappers) {
String resource = mapper.attributeValue("resource");
loadMapperXml(resource);
}
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 加载指定mapper映射配置
* @param resource 映射文件
*/
private void loadMapperXml(String resource) {
try {
InputStream in = Resources.getResourceAsStream(resource);
SAXReader reader = new SAXReader();
Document document = reader.read(in);
Element root = document.getRootElement();
String namespace = root.attributeValue("namespace");
List<Element> elements = root.elements();
for (Element e : elements) {
String id = e.attributeValue("id");
String resultType = e.attributeValue("resultType");
String sql = e.getText();
Mapper mapper = new Mapper();
mapper.setNamespace(namespace);
mapper.setId(id);
mapper.setResultType(resultType);
mapper.setSql(sql);
e.printStackTrace();
}
}
/**
* 创建c3p0数据源
*/
private void createDatasource() {
try {
datasource = new ComboPooledDataSource();
datasource.setDriverClass(this.driver);
datasource.setJdbcUrl(this.url);
datasource.setUser(this.name);
datasource.setPassword(this.pass);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
}
public Mapper getMapper(String namespace, String id) {
return mappers.get(namespace + "." + id);
}
public ComboPooledDataSource getDatasource() {
return datasource;
}
}
创建Mapper类, 封装 userMapper.xml 配置内容
/**
* 封装映射器配置.
* @author Jason
*/
public class Mapper {
// 映射器空间名
private String namespace;
// 查询语句标识
private String id;
// 返回值类型
private String resultType;
// 执行SQL
private String sql;
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
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 getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
}
4. 编写核心组件
SqlSession提供getMapper()方法获取代理对象
import java.lang.reflect.Proxy;
public class SqlSession {
public <T> T getMapper(Class<T> type) {
return (T) Proxy.newProxyInstance(
this.getClass().getClassLoader(),
new Class[]{type},
new MapperProxyFactory());
}
}
MapperProxyFactory实现 代理对象 的方法调用
import com.itheima.mybatis.day01.custom.kit.Execute;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MapperProxyFactory implements InvocationHandler {
/**
* 代理对象方法的调用
* @param proxy 代理对象
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 该方法名称就是mapper映射配置的SQL标识
String id = method.getName();
// 获取接口名称也就是映射配置中的名称空间
String namespace = method.getDeclaringClass().getName();
// 1. 加载配置
Configuration configuration = new Configuration();
// 2. 获取映射器
Mapper mapper = configuration.getMapper(namespace, id);
// 3. 执行操作
return Execute.selectList(mapper,
configuration.getDatasource().getConnection());
}
}
5.编写执行工具
/**
* 查询集合对象工具.
*
* @param mapper the mapper
* @param con the con
* @return the object
*/
public static <T> List<T> selectList(Mapper mapper, Connection con) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 1. 执行操作
String sql = mapper.getSql();
Class<?> aClass = Class.forName(mapper.getResultType());
ps = con.prepareStatement(sql);
rs = ps.executeQuery();
// 2. 处理结果
List<T> list = handler(rs, aClass);
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
// 3. 关闭资源
close(rs, ps, con);
}
return null;
}
private static List handler(ResultSet rs, Class<?> aClass) {
List list = new ArrayList();
try {
ResultSetMetaData metadata = rs.getMetaData();
// 元数据列数
// 反射对象(User)
Object obj = aClass.newInstance();
for (int i = 1; i <= count; i++) {
// 获取列名
String columnName = metadata.getColumnName(i);
// 获取列值
Object val = rs.getObject(columnName);
// 设置属性
Field field = aClass.getDeclaredField(columnName);
field.setAccessible(true);
field.set(obj, val);
}
list.add(obj);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
7. 测试入口
import com.itheima.mybatis.day01.custom.core.SqlSession;
import com.itheima.mybatis.day01.custom.core.SqlSessionFactory;
import com.itheima.mybatis.day01.custom.mapper.UserMapper;
import com.itheima.mybatis.day01.custom.model.User;
import org.junit.Test;
import java.util.List;
/**
* MyBatis案例程序
*
* @author Jason
*/
public class Main {
@Test
public void testCore(){
SqlSession sqlSession = SqlSessionFactory.openSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> all = userMapper.findAll();
for (User u : all){
System.out.println(u);
}
}
}
六、小结
相关技术的运用:
- dom4j
- mysql
- c3p0
- junit
- 工厂模式
- 反射
- 代理
- 泛型