实现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 ) ;
}