🎄MyBatis核心流程
🎄环境搭建
创建Maven项目
引入需要的依赖
<dependencies>
<!--引入dom4j 解析xml文件-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<!--引入mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!--lombok 简化entity开发 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
<!--junit依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
创建表和实体类
创建
DuckMapper
数据访问接口类
package com.bbm.mapper;
import com.bbm.entity.Duck;
/**
@author Liu Xianmeng
@createTime 2023/10/14 11:34
@instruction 鸭子数据访问接口
*/
public interface DuckMapper {
//查询方法
public Duck getDuckById(Integer id);
}
创建映射sql文件
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.bbm.mapper.DuckMapper">
<!--实现配置接口方法getDuckById-->
<select id="getDuckById" resultType="com.bbm.entity.Duck">
select * from duck where id = ?
</select>
</mapper>
创建mybatis的配置文件
bigbigmeng-mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?>
<database>
<!-- 配置连接数据库的信息 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/employees?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</database>
🎄实现MyBatis
Function类实现
创建
Function
类 对应DuckMapper.xml
文件中的方法
package com.bbm.mybatis.config;
/**
@author Liu Xianmeng
@createTime 2023/10/14 12:18
@instruction
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Function {
private String sqlType; // sql类型 比如select insert update delete
private String funcName; // 方法名
private String sql; // 执行的sql语句
private Object resultType; // 返回类型
private String parameterType; // 参数类型
}
MapperBean类实现
package com.bbm.mybatis.config;
/**
@author Liu Xianmeng
@createTime 2023/10/14 12:22
@instruction 将mapper数据访问接口类DuckMapper的信息进行封装
如接口类名、接口中的所有方法
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MapperBean {
//接口名
private String interfaceName;
//接口下的所有方法-集合
private List<Function> functions;
}
MyBatisSqlSessionConfig类实现
创建MyBatis的配置类
MyBatisSqlSessionConfiguration
读取xml配置文件 -> 创建数据库的连接
package com.bbm.mybatis.sqlSession;
/**
@author Liu Xianmeng
@createTime 2023/10/14 12:16
@instruction 读取bigbigmeng-mybatis.xml文件 创建数据库连接
*/
public class MyBatisSqlSessionConfig {
// 类的加载器
private static ClassLoader loader = ClassLoader.getSystemClassLoader();
/**
* 读取数据源信息 并返回数据库连接
* @param resource
* @return
*/
public Connection build(String resource) {
// 先创建一个空的sql连接
Connection connection = null;
try {
// 加载bigbigmeng-mybatis.xml文件 获取到对应的InputStream
InputStream stream = loader.getResourceAsStream(resource);
// 解析bigbigmeng-mybatis.xml文件 => dom4j
SAXReader reader = new SAXReader();
Document document = reader.read(stream);
//获取到bigbigmeng-mybatis.xml文件 的根元素 <database>
Element root = document.getRootElement();
System.out.println("C MyBatisSqlSessionConfig M build() -> root=" + root);
//解析root元素 返回Connection 这个readDataSource在下面单独写
connection = readDataSource(root);
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
/**
* 这个方法会解析bigbigmeng-mybatis.xml 并返回Connection数据库连接
* @param element
* @return
*/
private Connection readDataSource(Element element) {
// 根节点的名字应该是 "database"
if (!"database".equals(element.getName())) {
throw new RuntimeException("root节点应该是<database>");
}
// 连接DB的必要属性参数
String driverClassName = null;
String url = null;
String username = null;
String password = null;
// 遍历node下的子节点 获取属性值
for (Object item : element.elements("property")) {
// 获取数据库连接的属性
Element property = (Element) item;
String name = property.attributeValue("name");
String value = property.attributeValue("value");
//判断是否得到name和value
if (name == null || value == null) {
throw new RuntimeException("property节点没有设置name或者value属性");
}
// 进行参数设置
switch (name) {
case "url":
url = value;
break;
case "username":
username = value;
break;
case "driverClassName":
driverClassName = value;
break;
case "password":
password = value;
break;
default:
throw new RuntimeException("属性名没有匹配到...");
}
}
// 先创建一个空的连接
Connection connection = null;
try {
// 通过JDBC获取数据库连接
Class.forName(driverClassName);
connection = DriverManager.getConnection(url,username,password);
} catch (Exception e) {
e.printStackTrace();
}
//返回Connection
return connection;
}
/**
* 根据传入的mapper映射文件的路径 找到对应的mapper.xml文件 读取并构建对应的MapperBean对象
* @param path
* @return 返回MapperBean对象
*/
public MapperBean readMapper(String path) {
MapperBean mapperBean = new MapperBean();
try {
// 获取到xml文件对应的InputStream
InputStream stream = loader.getResourceAsStream(path);
SAXReader reader = new SAXReader();
// 获取到xml文件对应的document-dom4j
Document document = reader.read(stream);
/* 得到xml文件的根元素/根节点
<mapper namespace="com.bbm.mapper.DuckMapper">
<!--实现配置接口方法getMonsterById-->
<select id="getDuckById" resultType="com.bbm.entity.Duck">
select * from duck where id = ?
</select>
</mapper>
*/
Element root = document.getRootElement();
// 获取到namespace -> com.bbm.mapper.DuckMapper
String namespace = root.attributeValue("namespace").trim();
// 设置mapperBean的属性interfaceName
mapperBean.setInterfaceName(namespace);
// 得到root的迭代器 可以遍历它的子节点/子元素 生成Function
Iterator rootIterator = root.elementIterator();
// 保存接口下所有的方法信息
List<Function> list = new ArrayList<>();
// 遍历它的子节点/子元素 生成Function
while (rootIterator.hasNext()) {
// 取出一个子元素 dom4j Element
/*
<!--实现配置接口方法getDuckById-->
<select id="getDuckById" resultType="com.bbm.entity.Duck">
select * from duck where id = ?
</select>
*/
Element e = (Element) rootIterator.next();
Function function = new Function();
String sqlType = e.getName().trim();
String funcName = e.attributeValue("id").trim();
// resultType是返回类型的全路径 即全类名
String resultType = e.attributeValue("resultType").trim();
String sql = e.getText().trim();
// 开始封装
function.setSql(sql);
function.setFuncName(funcName);
function.setSqlType(sqlType);
// 使用反射生成一个返回类型对象 并setResultType
Object newInstance = Class.forName(resultType).newInstance();
function.setResultType(newInstance);
// 将封装好的function对象加入到 list
list.add(function);
}
// while循环结束后 将function的list设置
mapperBean.setFunctions(list);
} catch (Exception e) {
e.printStackTrace();
}
return mapperBean;
}
}
创建Executor执行器接口
package com.bbm.mybatis.sqlSession;
/**
@author Liu Xianmeng
@createTime 2023/10/14 12:47
@instruction
*/
public interface Executor {
//泛型方法
public <T> T query(String statement, Object parameter);
}
创建Executor执行器实现类
package com.bbm.mybatis.sqlSession;
/**
@author Liu Xianmeng
@createTime 2023/10/14 12:48
@instruction
*/
public class BBMExecutor implements Executor {
// 持有一个数据源配置对象
private MyBatisSqlSessionConfig myBatisSqlSessionConfig = new MyBatisSqlSessionConfig();
// 编写方法 通过MyBatisSqlSessionConfig对象 返回连接
private Connection getConnection() {
Connection connection = myBatisSqlSessionConfig.build("mapper/hsp_mybatis.xml");
return connection;
}
@Override
public <T> T query(String statement, Object parameter) {
//得到连接Connection
Connection connection = getConnection();
//查询返回的结果集
ResultSet set = null;
PreparedStatement pre = null;
try {
pre = connection.prepareStatement(statement);
// 设置参数 如果参数多 可以使用数组处理
pre.setString(1, parameter.toString());
set = pre.executeQuery();
// set数据封装到对象duck
// 这里做简化处理 -> 认为返回的结果就是一个monster记录 完善的写法是一套jdbc反射机制
Duck duck = new Duck();
// 遍历结果集 把数据封装到duck对象
while (set.next()) {
duck.setId(set.getInt("id"));
duck.setName(set.getString("name"));
duck.setAge(set.getInt("age"));
duck.setBirthday(set.getDate("birthday"));
}
return (T) duck;
} catch (Exception throwables) {
throwables.printStackTrace();
} finally {
try {
if (set != null) {
set.close();
}
if (pre != null) {
pre.close();
}
if (connection != null) {
connection.close();
}
} catch (Exception throwables) {
throwables.printStackTrace();
}
}
return null;
}
}