1.前言
通过模仿MyBatis源码手写自定义持久层框架,命名规则,设计规范均参考MyBatis.
2.自定义框架设计思路
使用端:
提供配置文件
1.SqlMapConfig.xml
核心配置文件,存放数据源,引入mapper
2.Mapper.xml
自定义sql
3.创建实体类User
4.创建dao层接口
5.创建测试类进行测试
框架端:
1.读取配置文件(以流的形式保存在内存中)
1.创建Resources类:
静态方法:static InputStream getResourceAsStream(String path)
根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中
2.创建Configuration类:
对应SqlMapConfig.xml
3.创建MappedStatement类:
对应Mapper.xml
2.解析配置文件(从内存中读取保存在javabean中)
1.创建XMLConfigBuilder类:(封装Configuration)
方法:Configutation parseConfig(InputStream inputStream)
2.创建XMLMapperBuilder类:(封装MappedStatement,需要传入封装Configuration)
方法:void parse(InputStream inputStream)
3.创建 SqlSessionFactoryBuilder类:(Builder构建者设计模式)
方法:SqlSessionFactory bulid()
2.创建 SqlSessionFactory接口:(工厂模式)
方法:SqlSession openSession();
3.创建SqlSession接口(封装具体CRUD方法)
方法:selectList(String statementid,Object... params);
selectOne(String statementid,Object... params);
3.自定义框架设计实现
3.1前提准备
1.创建两个Module
1)IPersistence
2)IPersistence_test
包名结构如图
2.pom.xml文件配置
1:IPersistence
mysql
mysql-connector-java
5.1.17
c3p0
c3p0
0.9.1.2
log4j
log4j
1.2.12
junit
junit
4.10
dom4j
dom4j
1.6.1
jaxen
jaxen
1.1.6
2:IPersistence_test
使用端只需要引入自定义框架
com.lagou
IPersistence
1.0-SNAPSHOT
3.IPersistence_test pojo包下新增实体User
public class User {
private Integer id;
private String username;
private String password;
private String birthday;
//省略getter setter 以及toString 方法
}
4.IPersistence_test dao包下新增接口IUserDao
public interface IUserDao {
//查询所有
public List findAll();
//条件查询
public User findUserByCondition(User user);
//新增
public void addUser(User user);
//更新
public void updateUser(User user);
//删除
public void deleteUserById(User user);
}
3.2配置文件准备
定义在IPersistence_test下的resources目录
所有节点名称均为自定义名称,没有约束头文件
1.核心配置文件:sqlMapConfig.xml
2.SQL语句文件:UserMapper.xml
namespace:该Mapper的唯一标识
id:sql的唯一标识
namespace+id:表示statementId(后面会用到)
resultType:返回值的全限定类名
paramterType:参数的全限定类名
#{}:表示占位符号
3.3读取配置文件
1.创建Javabean
MappedStatement
对应mapper.xml中的一个select标签
public class MappedStatement {
//id标识
private String id;
//返回值类型
private String resultType;
//参数值类型
private String paramterType;
//标签中的sql语句
private String sql;
//省略getter setter 以及toString方法
}
Configuration
对应sqlMapConfig.xml
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
public class Configutation {
//数据源
private DataSource dataSource;
Map mappedStatementMap = new HashMap<>();
//省略getter setter 以及toString方法
}
因为sql标签存在多个,因此需要使用Map集合
Map key:statementid(上文提及) value:封装好的MappedStatement对象
2.创建Resources类
根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中
public class Resources {
public static InputStream getResourceAsStream(String path){
InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
return resourceAsStream;
}
}
3.4解析配置文件
1.创建XMLConfigBuilder类
package com.lagou.config;
import com.lagou.io.Resources;
import com.lagou.pojo.Configutation;
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.List;
import java.util.Properties;
/**
* @author 振帅
* @create 2020/11/6 18:54
* @description: XMLConfigBuilder 解析配置文件
*/
public class XMLConfigBuilder {
private Configutation configutation;
public XMLConfigBuilder(){
this.configutation = new Configutation();
}
/**
*该方法就是使用dom4j将配置文件进行解析 封装Configuration
*/ public Configutation parseConfig(InputStream inputStream) throws DocumentException, PropertyVetoException {
Document document = new SAXReader().read(inputStream);
//
Element rootElement = document.getRootElement();
List list = rootElement.selectNodes("//property");
//用Map也可以
Properties properties = new Properties();
for (Element element : list) {
String name = element.attributeValue("name");
String value = element.attributeValue("value");
properties.setProperty(name,value);
}
//数据库连接池
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
comboPooledDataSource.setUser(properties.getProperty("username"));
comboPooledDataSource.setPassword(properties.getProperty("password"));
configutation.setDataSource(comboPooledDataSource);
//mapper.xml解析:拿到路径-字节输入流--dom4j解析
List mapperList = rootElement.selectNodes("//mapper");
for (Element element : mapperList) {
String mapperPath = element.attributeValue("resource");
InputStream resourceAsStream = Resources.getResourceAsStream(mapperPath);
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configutation);
xmlMapperBuilder.parse(resourceAsStream);
}
return configutation;
}
}
主要是parseConfig方法,将InputStream转化为Configutation。
2.创建XMLMapperBuilder类
package com.lagou.config;
import com.lagou.pojo.Configutation;
import com.lagou.pojo.MappedStatement;
import com.lagou.pojo.MappedStatementEnum;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.List;
/**
* @author 振帅
* @create 2020/11/6 19:35
* @description: XMLMapperBuilder
*/
public class XMLMapperBuilder {
private Configutation configutation;
public XMLMapperBuilder(Configutation configutation){
this.configutation = configutation;
}
public void parse(InputStream inputStream) throws DocumentException {
Document document = new SAXReader().read(inputStream);
//
Element rootElement = document.getRootElement();
String namespace = rootElement.attributeValue("namespace");
List list = rootElement.selectNodes("//select");
for (Element element : list) {
String id = element.attributeValue("id");
String resultType = element.attributeValue("resultType");
String paramterType = element.attributeValue("paramterType");
String sqlText = element.getTextTrim();//去除空格
MappedStatement mappedStatement = new MappedStatement();
mappedStatement.setId(id);
mappedStatement.setResultType(resultType);
mappedStatement.setParamterType(paramterType);
mappedStatement.setSql(sqlText);
mappedStatement.setMappedStatementEnumEnum(MappedStatementEnum.SELECT);
String key = namespace + "." + id;
configutation.getMappedStatementMap().put(key,mappedStatement);
}
}
}
封装MappedStatement
构建statementId
完善Configutation 的 mappedStatementMap
3.创建SqlSessionFactoryBuilder类
package com.lagou.sqlSession;
import com.lagou.config.XMLConfigBuilder;
import com.lagou.pojo.Configutation;
import org.dom4j.DocumentException;
import java.beans.PropertyVetoException;
import java.io.InputStream;
/**
* @author 振帅
* @create 2020/11/6 18:49
* @description: SqlSesstionFactoryBuilder*/
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream in) throws PropertyVetoException, DocumentException {
// 第一:使用dom4j解析配置文件,将解析出来的内容封装到Configuration中
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
Configutation configutation = xmlConfigBuilder.parseConfig(in);
// 第二: 创建SqlSesstionFactory对象
DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configutation);
return defaultSqlSessionFactory;
}
}
4.创建 SqlSessionFactory接口
package com.lagou.sqlSession;
/**
* @author 振帅
* @create 2020/11/6 18:53
* @description: SqlSesstionFactory
*/
public interface SqlSessionFactory {
public SqlSession openSession();
}
5.创建 SqlSessionFactory默认实现类
package com.lagou.sqlSession;
import com.lagou.pojo.Configutation;
/**
* @author 振帅
* @create 2020/11/6 20:09
* @description: DefaultSqlSesstionFactory
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private Configutation configutation;
public DefaultSqlSessionFactory(Configutation configutation){
this.configutation = configutation;
}
@Override
public SqlSession openSession() {
return new DefaultSqlSession(configutation);
}
}
6.创建 SqlSession接口
package com.lagou.sqlSession;
import java.util.List;
/**
* @author 振帅
* @create 2020/11/6 20:14
* @description: SqlSesstion
*/
public interface SqlSession {
//查询所有
public List selectList(String statementid,Object... params) throws Exception;
//根据条件查询单个
public T selectOne(String statementid,Object... params) throws Exception;
//新增
public void update(String statementid,Object... params) throws Exception;
//删除
public void delete(String statementid,Object... params) throws Exception;
//为dao接口生成代理实现类
public T getMapper(Class> mapperClass);
}
7.创建 SqlSession默认实现类
package com.lagou.sqlSession;
import com.lagou.pojo.Configutation;
import com.lagou.pojo.MappedStatement;
import com.lagou.pojo.MappedStatementEnum;
import java.lang.reflect.*;
import java.util.List;
/**
* @author 振帅
* @create 2020/11/6 20:16
* @description: DefaultSqlSession */public class DefaultSqlSession implements SqlSession {
private Configutation configutation;
public DefaultSqlSession(Configutation configutation){
this.configutation = configutation;
}
@Override
public List selectList(String statementid, Object... params) throws Exception {
SimpleExecutor simpleExecutor = new SimpleExecutor();
MappedStatement mappedStatement = configutation.getMappedStatementMap().get(statementid);
List
3.2测试类编写
PS:我喜欢从使用入手,思路会比较清楚,报红先忍着
在IPersistence_test test包下新建IPersistenceTest.java
我们希望自定义框架能够拥有种MyBatis的类似的功能
1.定义接口IUserDao 不用写Dao的实现(代理模式)
public class IPersistenceTest{
//读取到的字节流对象
private InputStream resourceAsStream;
private SqlSessionFactory sqlSesstionFactory;
private SqlSession sqlSession;
private IUserDao userDao;
@Before
public void before() throws Exception {
resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//构建者设计模式获取sqlSesstionFactory
sqlSesstionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = sqlSesstionFactory.openSession();
userDao = sqlSession.getMapper(IUserDao.class);
}
@Test
public void test1() throws Exception {
User user = new User();
user.setId(1);
user.setUsername("lucy");
//普通模式
List userList = sqlSession.selectList("com.lagou.dao.IUserDao.findUserByCondition",user);
for (User user1 : userList) {
System.out.println(user1);
}
}
@Test
public void test2() throws Exception {
User user = new User();
user.setId(1);
user.setUsername("lucy");
//代理模式
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
User userByCondition = userDao.findUserByCondition(user);
System.out.println(userByCondition);
}
}
@Before 注解可以定义在所有Test文件之前,在测试之前执行
通过Resources.getResourceAsStream("sqlMapConfig.xml")方法,将读取到的字节流对象保存在成员变量上。
涉及到的设计模式:
Builder构建者设计模式、工厂模式、代理模式