实现MyBatis核心机制环境搭建
1.核心框架示意图
2.模块搭建
1.创建maven项目
2.引入依赖
<?xml version="1.0" encoding="UTF-8"?>
< project xmlns = " http://maven.apache.org/POM/4.0.0" xmlns: xsi= " http://www.w3.org/2001/XMLSchema-instance" xsi: schemaLocation= " http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > < modelVersion> 4.0.0</ modelVersion> < parent> < groupId> com.sunxiansheng</ groupId> < artifactId> core_mechanisms</ artifactId> < version> 1.0-SNAPSHOT</ version> </ parent> < artifactId> sun-mybatis</ artifactId> < properties> < maven.compiler.source> 8</ maven.compiler.source> < maven.compiler.target> 8</ maven.compiler.target> < project.build.sourceEncoding> UTF-8</ project.build.sourceEncoding> </ properties> < dependencies> < dependency> < groupId> dom4j</ groupId> < artifactId> dom4j</ artifactId> < version> 1.6.1</ version> </ dependency> < dependency> < groupId> mysql</ groupId> < artifactId> mysql-connector-java</ artifactId> < version> 8.0.22</ version> </ dependency> < dependency> < groupId> org.projectlombok</ groupId> < artifactId> lombok</ artifactId> < version> 1.18.30</ version> </ dependency> < dependency> < groupId> junit</ groupId> < artifactId> junit</ artifactId> < version> 4.13.2</ version> </ dependency> </ dependencies> </ project>
3.连接数据库
4.数据库表设计
CREATE DATABASE ` sun_mybatis` ;
USE ` sun_mybatis` ; CREATE TABLE ` monster`
( ` id` INT NOT NULL AUTO_INCREMENT , ` age` INT NOT NULL , ` birthday` DATE DEFAULT NULL , ` email` VARCHAR ( 255 ) NOT NULL , ` gender` TINYINT NOT NULL , ` name` VARCHAR ( 255 ) NOT NULL , ` salary` DOUBLE NOT NULL , PRIMARY KEY ( ` id` )
) CHARSET = utf8; INSERT INTO ` monster` ( ` age` , ` birthday` , ` email` , ` gender` , ` name` , ` salary` )
VALUES ( 25 , '1998-01-15' , 'example@example.com' , 1 , 'John Doe' , 50000.00 ) ;
3.设计图
读取配置文件,得到数据库连接
1.目录
2.sun-config.xml 配置文件
<?xml version="1.0" encoding="utf-8" ?>
< database> < property name = " driverClassName" value = " com.mysql.cj.jdbc.Driver" /> < property name = " url" value = " jdbc:mysql://bj--grp-.sql.tencentcdb.com:24169/sun_mybatis?useSSL=false& useUnicode=true& characterEncoding=UTF-8" /> < property name = " username" value = " " /> < property name = " password" value = " " />
</ database>
3.SunConfiguration.java 读取配置文件获取数据库连接
package com. sunxiansheng. mybatis. sqlsession ; import org. dom4j. Document ;
import org. dom4j. DocumentException ;
import org. dom4j. Element ;
import org. dom4j. io. SAXReader ;
import org. slf4j. Logger ;
import org. slf4j. LoggerFactory ; import java. io. InputStream ;
import java. sql. Connection ;
import java. sql. DriverManager ;
import java. sql. SQLException ;
import java. util. List ;
public class SunConfiguration { private static final Logger log = LoggerFactory . getLogger ( SunConfiguration . class ) ; private static ClassLoader loader = ClassLoader . getSystemClassLoader ( ) ; public Connection build ( String resource) { InputStream resourceAsStream = loader. getResourceAsStream ( resource) ; SAXReader saxReader = new SAXReader ( ) ; try { Document document = saxReader. read ( resourceAsStream) ; Element rootElement = document. getRootElement ( ) ; List < Element > property = rootElement. elements ( "property" ) ; String driverClassName = null ; String url = null ; String username = null ; String password = null ; for ( Element element : property) { String name = element. attributeValue ( "name" ) ; String value = element. attributeValue ( "value" ) ; switch ( name) { case "driverClassName" : driverClassName = value; log. info ( "加载驱动类:{}" , value) ; break ; case "url" : url = value; log. info ( "加载url:{}" , value) ; break ; case "username" : username = value; log. info ( "加载用户名:{}" , value) ; break ; case "password" : password = value; log. info ( "加载密码:{}" , value) ; break ; default : throw new RuntimeException ( "未知的property属性名" ) ; } } Class . forName ( driverClassName) ; return DriverManager . getConnection ( url, username, password) ; } catch ( DocumentException | ClassNotFoundException | SQLException e) { log. error ( "解析xml配置文件失败" , e) ; throw new RuntimeException ( e) ; } } }
4.SunConfigurationTest.java 测试
package com. sunxiansheng. mybatis. sqlsession ; import org. junit. Test ; import java. sql. Connection ;
public class SunConfigurationTest { @Test public void build ( ) { SunConfiguration sunConfiguration = new SunConfiguration ( ) ; Connection build = sunConfiguration. build ( "sun-config.xml" ) ; System . out. println ( "build = " + build) ; } }
使用SunExecutor来执行SQL
1.目录
2.Monster.java
package com. sunxiansheng. mybatis. entity ; import lombok. Data ; import java. io. Serializable ;
import java. util. Date ; @Data
public class Monster implements Serializable { private Integer id; private Integer age; private Date birthday; private String email; private Byte gender; private String name; private Double salary; private static final long serialVersionUID = 1L ;
}
3.Executor.java
package com. sunxiansheng. mybatis. sqlsession ;
public interface Executor { public < T > T query ( String sql, Object parameter) ; }
4.SunExecutor.java 具体的执行器
package com. sunxiansheng. mybatis. sqlsession ; import com. sunxiansheng. mybatis. entity. Monster ;
import org. slf4j. Logger ;
import org. slf4j. LoggerFactory ; import java. sql. Connection ;
import java. sql. PreparedStatement ;
import java. sql. ResultSet ;
public class SunExecutor implements Executor { public static final SunConfiguration configuration = new SunConfiguration ( ) ; private static final Logger log = LoggerFactory . getLogger ( SunExecutor . class ) ; @Override public < T > T query ( String sql, Object parameter) { Connection connection = configuration. build ( "sun-config.xml" ) ; log. info ( "获取连接:{}" , connection) ; ResultSet resultSet = null ; PreparedStatement preparedStatement = null ; Monster monster = new Monster ( ) ; try { preparedStatement = connection. prepareStatement ( sql) ; preparedStatement. setString ( 1 , parameter. toString ( ) ) ; resultSet = preparedStatement. executeQuery ( ) ; while ( resultSet. next ( ) ) { monster. setId ( resultSet. getInt ( "id" ) ) ; monster. setAge ( resultSet. getInt ( "age" ) ) ; monster. setBirthday ( resultSet. getDate ( "birthday" ) ) ; monster. setEmail ( resultSet. getString ( "email" ) ) ; monster. setGender ( resultSet. getByte ( "gender" ) ) ; monster. setName ( resultSet. getString ( "name" ) ) ; monster. setSalary ( resultSet. getDouble ( "salary" ) ) ; } log. info ( "查询结果:{}" , monster) ; } catch ( Exception e) { throw new RuntimeException ( e) ; } finally { try { if ( resultSet != null ) { resultSet. close ( ) ; } if ( preparedStatement != null ) { preparedStatement. close ( ) ; } if ( connection != null ) { connection. close ( ) ; } } catch ( Exception e) { throw new RuntimeException ( e) ; } } return ( T ) monster; } }
5.SunConfigurationTest.java 测试
@Test
public void executor ( ) { SunExecutor sunExecutor = new SunExecutor ( ) ; Monster monster = sunExecutor. query ( "select * from monster where id = ?" , 1 ) ;
}
将SqlSession封装到执行器
1.目录
2.SunSqlSession.java
package com. sunxiansheng. mybatis. sqlsession ;
public class SunSqlSession { private Executor executor = new SunExecutor ( ) ; private SunConfiguration configuration = new SunConfiguration ( ) ; public < T > T selectOne ( String statement, Object parameter) { return executor. query ( statement, parameter) ; }
}
3.测试
@Test
public void selectOne ( ) { SunSqlSession sunSqlSession = new SunSqlSession ( ) ; Monster monster = sunSqlSession. selectOne ( "select * from monster where id = ?" , 1 ) ; System . out. println ( "monster = " + monster) ;
}
开发MapperBean和Function
1.目录
2.MonsterMapper.java
package com. sunxiansheng. mapper ; import com. sunxiansheng. entity. Monster ;
public interface MonsterMapper { public Monster getMonsterById ( Integer id) ; }
3.MonsterMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
< mapper namespace = " com.sunxiansheng.mapper.MonsterMapper" > < select id = " getMonsterById" resultType = " com.sunxiansheng.entity.Monster" > select * from monster where id = ?</ select>
</ mapper>
4.Function.java 记录对应的mapper.xml的方法信息
package com. sunxiansheng. mybatis. config ; import lombok. Data ;
@Data
public class Function { private String sqlType; private String funcName; private String sql; private Object resultType; }
5.MapperBean.java 将mapper接口信息进行封装
package com. sunxiansheng. mybatis. config ; import java. util. List ;
public class MapperBean { private String interfaceName; private List < Function > functions; }
读取xml文件解析MapperBean
1.目录
2.Function.java 加链式调用注解
3.MapperBean.java 加Data注解
4. SunConfiguration.java 增加解析MapperBean的方法
public MapperBean readMapper ( String path) { MapperBean mapperBean = new MapperBean ( ) ; ClassLoader loader = ClassLoader . getSystemClassLoader ( ) ; InputStream resourceAsStream = loader. getResourceAsStream ( path) ; SAXReader saxReader = new SAXReader ( ) ; try { Document document = saxReader. read ( resourceAsStream) ; Element rootElement = document. getRootElement ( ) ; String namespace = rootElement. attributeValue ( "namespace" ) ; List < Element > elements = rootElement. elements ( ) ; List < Function > functions = elements. stream ( ) . map ( element -> { Function function = new Function ( ) ; String sqlType = element. getName ( ) ; String funcName = element. attributeValue ( "id" ) ; String resultType = element. attributeValue ( "resultType" ) ; String sql = element. getTextTrim ( ) ; function. setSqlType ( sqlType) . setFuncName ( funcName) . setResultType ( resultType) . setSql ( sql) ; return function; } ) . collect ( Collectors . toList ( ) ) ; mapperBean. setInterfaceName ( namespace) ; mapperBean. setFunctions ( functions) ; } catch ( DocumentException e) { throw new RuntimeException ( e) ; } log. info ( "mapperBean:{}" , mapperBean) ; return mapperBean;
}
5.SunConfigurationTest.java 测试
@Test public void readMapper ( ) { SunConfiguration sunConfiguration = new SunConfiguration ( ) ; sunConfiguration. readMapper ( "MonsterMapper.xml" ) ; }
动态代理Mapper方法
1.目录
2.MapperProxyFactory.java 代理工厂,可以获取接口的代理对象
package com. sunxiansheng. mybatis. sqlsession ; import java. lang. reflect. Proxy ;
public class MapperProxyFactory { public static < T > T getMapperProxy ( Class < T > clazz, SunSqlSession sunSqlSession, SunConfiguration sunConfiguration) { return ( T ) Proxy . newProxyInstance ( clazz. getClassLoader ( ) , new Class [ ] { clazz} , new SunMapperProxy ( sunSqlSession, clazz, sunConfiguration) ) ; } }
3.SunMapperProxy.java 代理逻辑
package com. sunxiansheng. mybatis. sqlsession ; import com. sunxiansheng. mybatis. config. Function ;
import com. sunxiansheng. mybatis. config. MapperBean ;
import org. slf4j. Logger ;
import org. slf4j. LoggerFactory ; import java. lang. reflect. InvocationHandler ;
import java. lang. reflect. Method ;
import java. util. List ;
public class SunMapperProxy implements InvocationHandler { private static final Logger log = LoggerFactory . getLogger ( SunMapperProxy . class ) ; private SunSqlSession sunSqlSession; private String mapperFile; private SunConfiguration sunConfiguration; public SunMapperProxy ( SunSqlSession sunSqlSession, Class < ? > mapperFile, SunConfiguration sunConfiguration) { this . sunSqlSession = sunSqlSession; this . mapperFile = mapperFile. getSimpleName ( ) + ".xml" ; this . sunConfiguration = sunConfiguration; } @Override public Object invoke ( Object proxy, Method method, Object [ ] args) throws Throwable { String methodName = method. getName ( ) ; MapperBean readMapper = sunConfiguration. readMapper ( mapperFile) ; List < Function > functions = readMapper. getFunctions ( ) ; Function targetFunction = null ; for ( Function function : functions) { if ( methodName. equals ( function. getFuncName ( ) ) ) { targetFunction = function; break ; } } String sqlType = targetFunction. getSqlType ( ) ; String sql = targetFunction. getSql ( ) ; if ( sqlType. equals ( "select" ) ) { Object selected = sunSqlSession. selectOne ( sql, args[ 0 ] ) ; log. info ( "invoke查询结果:{}" , selected) ; return selected; } return null ; }
}
4.测试
@Test
public void getResult ( ) { MonsterMapper mapperProxy = MapperProxyFactory . getMapperProxy ( MonsterMapper . class , new SunSqlSession ( ) , new SunConfiguration ( ) ) ; Monster monsterById = mapperProxy. getMonsterById ( 1 ) ;
}