网站首页 > 技术文章 正文
本章我们将可以自己编写代码,模拟MyBatis的简单实现
先回顾MyBatis的实现过程:
1)通过SQLSessionFactoryBuilder创建SQLSessionFactory时,将核心配置文件中configuration节点的内容,解析到SQLSessionFactory中
2)通过SQLSessionFactory获得SqlSession时,返回DefaultSqlSession
3)调用SQLSessionFactory的getMapper方法时,返回了DAO接口的代理对象,代理对象在invoke方法中实现了增删改查操作
4)具体的增删改查操作通过JDBC+SQL实现
下面我们模拟实现上面的过程:
1、模拟配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<environments default="develop">
<environment id="develop">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test_db"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--<package name="com.qf.mbs.mapper"/>-->
<mapper resource="com/qf/mbs/mapper/UserDAO.xml"/>
<mapper class="com.qf.mbs.dao.UserDAO"/>
</mappers>
</configuration>
DAO接口:
public interface UserDAO {
List<User> findAll();
User findById(Integer id);
void addUser(User user);
void updateUser(User user);
void deleteUser(Integer id);
}
Mapper文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mbs.dao.UserDAO">
<insert id="addUser" parameterType="com.qf.mbs.po.User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO USER (USERNAME,BIRTHDAY,SEX,ADDRESS) VALUES (#{username},#{birthday},#{sex},#{address})
</insert>
<update id="updateUser" parameterType="com.qf.mbs.po.User">
UPDATE USER set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
</update>
<delete id="deleteUser" parameterType="java.lang.Integer">
DELETE FROM USER where id=#{id}
</delete>
<select id="findById" parameterType="java.lang.Integer" resultType="com.qf.mbs.po.User">
SELECT * FROM USER where id=#{id}
</select>
<select id="findAll" resultType="com.qf.mbs.po.User">
select * from user
</select>
</mapper>
/**
配置文件的解析类
*/
public class ConfigParser {
//保存数据源信息
private Map<String,String> dataSource = new HashMap<>();
//保存映射文件路径
private List<String> resourceMappers = new ArrayList<>();
//保存class映射类名
private List<String> classMappers = new ArrayList<>();
public void parseConfig(InputStream inputStream) throws DocumentException {
SAXReader reader = new SAXReader();
reader.setEntityResolver(new EntityResolver() {
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
return new InputSource(new ByteArrayInputStream("".getBytes()));
}
});
Document doc = reader.read(inputStream);
Element rootElement = doc.getRootElement();
List<Element> properties = rootElement.element("environments").element("environment").element("dataSource").elements();
for(Element property : properties){
if("driver".equals(property.attributeValue("name"))){
dataSource.put("driver",property.attributeValue("value"));
}
if("url".equals(property.attributeValue("name"))){
dataSource.put("url",property.attributeValue("value"));
}
if("username".equals(property.attributeValue("name"))){
dataSource.put("username",property.attributeValue("value"));
}
if("password".equals(property.attributeValue("name"))){
dataSource.put("password",property.attributeValue("value"));
}
}
List<Element> mapperEles = rootElement.element("mappers").elements();
for(Element mapper : mapperEles){
if(mapper.attribute("resource") != null){
resourceMappers.add(mapper.attributeValue("resource"));
}
if(mapper.attribute("class") != null){
classMappers.add(mapper.attributeValue("class"));
}
}
}
public Map<String, String> getDataSource() {
return dataSource;
}
public List<String> getResourceMappers() {
return resourceMappers;
}
public List<String> getClassMappers() {
return classMappers;
}
}
测试:
@Test
public void testParse(){
ConfigParser parser = new ConfigParser();
try {
parser.parseConfig(Test2.class.getClassLoader().getResourceAsStream("mock-config.xml"));
System.out.println(parser.getDataSource());
System.out.println(parser.getClassMappers());
System.out.println(parser.getResourceMappers());
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
* SQL映射类
*/
public class Mapper {
//sql类型 insert update delete select
private String sqlType;
//Mapper方法名
private String methodName;
//sql语句
private String sql;
//参数类型
private String paramType;
//返回类型
private String returnType;
....
}
/**
映射文件的解析类
*/
public class MapperParser {
private Map<String,Mapper> mappers = new HashMap<>();
public void parseMappers(InputStream inputStream) throws DocumentException {
SAXReader reader = new SAXReader();
reader.setEntityResolver(new EntityResolver() {
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
return new InputSource(new ByteArrayInputStream("".getBytes()));
}
});
Document doc = reader.read(inputStream);
Element root = doc.getRootElement();
List<Element> elements = root.elements();
String namespace = root.attributeValue("namespace");
for(Element e : elements){
Mapper mapper = new Mapper();
mapper.setSqlType(e.getName());
mapper.setMethodName(e.attributeValue("id"));
mapper.setParamType(e.attributeValue("parameterType"));
mapper.setReturnType(e.attributeValue("resultType"));
mapper.setSql(e.getText());
mappers.put(namespace+"."+e.attributeValue("id"),mapper);
}
}
public Map<String, Mapper> getMappers() {
return mappers;
}
}
测试:
@Test
public void testParse(){
ConfigParser parser = new ConfigParser();
MapperParser mParser = new MapperParser();
try {
parser.parseConfig(Test2.class.getClassLoader().getResourceAsStream("mock-config.xml"));
System.out.println(parser.getDataSource());
System.out.println(parser.getClassMappers());
System.out.println(parser.getResourceMappers());
for(String resource : parser.getResourceMappers()){
System.out.println("resourc:"+resource);
mParser.parseMappers(Test2.class.getClassLoader().getResourceAsStream(resource));
System.out.println(mParser.getMappers());
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
* 模拟SqlSession
*/
public interface MySqlSession {
/**
* 返回Mapper对象
* @param clazz
* @param <T>
* @return
*/
<T> T getMapper(Class<T> clazz);
}
public class MySqlSessionImpl implements MySqlSession {
private Map<String,String> dataSource;
private Map<String,Mapper> mappers;
public MySqlSessionImpl(Map<String, String> dataSource, Map<String, Mapper> mappers) {
this.dataSource = dataSource;
this.mappers = mappers;
}
@Override
public <T> T getMapper(Class<T> clazz) {
XMLMapperProxy xmlMapperProxy = new XMLMapperProxy(dataSource,mappers,clazz);
return (T) xmlMapperProxy.getProxy();
}
}
/**
* XML映射文件的代理
*/
public class XMLMapperProxy implements InvocationHandler{
private Map<String,String> dataSource;
private Map<String,Mapper> mappers;
private Class<?> clazz;
private Connection connection;
public XMLMapperProxy(Map<String, String> dataSource, Map<String,Mapper> mappers,Class clazz) {
this.dataSource = dataSource;
this.mappers = mappers;
this.clazz = clazz;
initDatasource(dataSource);
}
/**
* 获得代理对象
* @return
*/
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),new Class[]{clazz},this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String key = clazz.getName() + "." + method.getName();
Mapper mapper = mappers.get(key);
System.out.println("mapper--->"+mapper);
if(mapper == null){
System.out.println(key+",has no mapper.");
return null;
}
PreparedStatement psmt = connection.prepareStatement(mapper.getSql());
Object result = null;
if("select".equals(mapper.getSqlType())){
ResultSet rs = psmt.executeQuery();
List<String> columns = new ArrayList<>();
int count = rs.getMetaData().getColumnCount();
for(int i = 1;i <= count;i++){
columns.add(rs.getMetaData().getColumnName(i));
}
List selectRs = new ArrayList();
while(rs.next()){
Class<?> poClazz = Class.forName(mapper.getReturnType());
Object po = poClazz.newInstance();
for(String colName : columns){
for(Method med : poClazz.getMethods()){
if(med.getName().equalsIgnoreCase("set"+colName)){
try{
med.invoke(po,rs.getString(colName));
}catch (IllegalArgumentException ex) {
med.invoke(po, rs.getInt(colName));
}
}
}
}
selectRs.add(po);
}
result = selectRs;
}else{
result = psmt.executeUpdate();
}
connection.close();
return result;
}
private void initDatasource(Map<String,String> dataSource){
try {
Class.forName(dataSource.get("driver"));
connection = DriverManager.getConnection(dataSource.get("url"),dataSource.get("username"),dataSource.get("password"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
工厂:
public class MySqlSessionFactory {
private String config;
public MySqlSessionFactory(String config) {
this.config = config;
}
public MySqlSession openSession(){
ConfigParser parser = new ConfigParser();
MapperParser mParser = new MapperParser();
try {
parser.parseConfig(MySqlSessionFactory.class.getClassLoader().getResourceAsStream(config));
System.out.println(parser.getDataSource());
System.out.println(parser.getClassMappers());
System.out.println(parser.getResourceMappers());
for(String resource : parser.getResourceMappers()){
System.out.println("resource:"+resource);
mParser.parseMappers(MySqlSessionFactory.class.getClassLoader().getResourceAsStream(resource));
System.out.println(mParser.getMappers());
}
return new MySqlSessionImpl(parser.getDataSource(),mParser.getMappers());
} catch (DocumentException e) {
e.printStackTrace();
}
return null;
}
}
工厂的创建器
public class MySqlSessionFactoryBuilder {
public MySqlSessionFactory build(String config){
return new MySqlSessionFactory(config);
}
}
测试:
@Test
public void testSelect(){
MySqlSessionFactory factory = new MySqlSessionFactoryBuilder().build("mock-config.xml");
MySqlSession mySqlSession = factory.openSession();
UserDAO mapper = mySqlSession.getMapper(UserDAO.class);
List<User> user = mapper.findAll();
System.out.println("user--->"+user);
}
到此我们按照MyBatis的源码,简单模拟了它的实现过程,这样我们会MyBatis这个框架会有更深入的理解。
- 上一篇: 成都校区*精品*Dom4J解析XML的范例浅析
- 下一篇: VUEX的store用法
猜你喜欢
- 2024-09-22 成都校区*精品*Dom4J解析XML的范例浅析
- 2024-09-22 Jenkins Nested View插件XXE漏洞(CVE-2021-21680)分析
- 2024-09-22 Java互联网架构-Spring IOC底层源码分析
- 2024-09-22 XML文件
- 2024-09-22 Dom4j解析xml的小demo
- 2024-09-22 java全栈CMS:AOP+Freemarke代码自动生成4
- 2024-09-22 专科生逆袭入职阿里P6,成人生赢家。网友:我应该也可以
- 2024-09-22 在IDEA的maven项目中连接使用MySQL8.0方法教程
- 2024-09-22 Spring:IOC 控制反转
- 2024-09-22 Dom4j解析xml
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)