一、使用dom4j解析配置文件
1.1、引入依赖
<?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>
<groupId>star.light</groupId>
<artifactId>mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- 依赖 -->
<dependencies>
<!-- dom4j依赖 -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<!-- jaxen依赖 -->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.2.0</version>
</dependency>
<!-- junit依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</project>
1.2、创建核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!-- environments:配置多个连接数据库环境 -->
<environments default="development">
<!-- environment:配置某个具体的环境 -->
<environment id="development">
<!-- 设置事务管理方式 -->
<transactionManager type="JDBC"/>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<!-- 设置连接数据库的驱动 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!-- 设置连接数据库的地址 -->
<property name="url" value="jdbc:mysql://localhost:3306/db_test"/>
<!-- 设置连接数据库的用户名 -->
<property name="username" value="root"/>
<!-- 设置连接数据库的密码 -->
<property name="password" value="abc123"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<!--
指定 XxxMapper.xml 配置文件的路径
resource属性自动会从类的根路径下开始查找资源
url属性从绝对路径当中加载资源,一般很少使用,语法格式如下:file:///绝对路径
-->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
1.3、创建映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="userMapper">
<!-- 添加一条用户信息 -->
<insert id="insertUser">
insert into t_user(id,username,password,age,sex,email)
values (#{id},#{username},#{password},#{age},#{sex},#{email})
</insert>
<!-- 根据id删除指定用户 -->
<delete id="deleteUserById">
delete from t_user where id = #{id}
</delete>
<!-- 根据id修改某条记录 -->
<update id="updateUserById">
update t_user set username = #{username},
password = #{password},
age = #{age},
sex = #{sex},
email = #{email}
where id = #{id}
</update>
<!-- 根据id查询用户信息 -->
<select id="selectUserById" resultType="star.light.pojo.User">
select id,username,password,age,sex,email from t_user where id = #{id}
</select>
<!-- 查询所有用户信息 -->
<select id="selectAllUser" resultType="star.light.pojo.User">
select id,username,password,age,sex,email from t_user
</select>
</mapper>
1.4、测试程序
package star.light.test;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class ParseByDom4jTest {
@Test
public void testParseMyBatisConfigXml() throws DocumentException {
// 创建SAXReader对象
SAXReader reader = new SAXReader();
// 获取输入流
InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("config.xml");
// 读XML文件,但会Document对象,Document对象是文档对象,代表了整个XML文件
Document document = reader.read(resourceAsStream);
// 获取default默认环境id
// 以下的xpath代表了:从根找<configuration>标签,然后找<configuration>标签下的子标签<environments>标签
String xpath = "/configuration/environments";
Element environments = (Element) document.selectSingleNode(xpath);
// 获取属性的值
String defaultEnvironmentId = environments.attributeValue("default");
// 获取具体的环境environment
xpath = "/configuration/environments/environment[@id='" + defaultEnvironmentId + "']";
Element environment = (Element) document.selectSingleNode(xpath);
// 获取enviroment节点下的transactionManager节点
Element transactionManager = environment.element("transactionManager");
String transactionType = transactionManager.attributeValue("type");
System.out.println("事务管理器的类型:" + transactionType);
// 获取dataSource节点
Element dataSource = environment.element("dataSource");
String dataSourceType = dataSource.attributeValue("type");
System.out.println("数据源的类型:" + dataSourceType);
// 获取dataSource节点下的所有子节点
List<Element> propertyElements = dataSource.elements();
propertyElements.forEach(propertyElement -> {
String name = propertyElement.attributeValue("name");
String value = propertyElement.attributeValue("value");
System.out.println(name + "=" + value);
});
// 获取所有的<mapper>标签
// 不从根下获取,从任意位置开始,获取所有的某个标签
xpath = "//mapper";
List<Node> mappers = document.selectNodes(xpath);
// 遍历
mappers.forEach(mapper -> {
Element mapperElement = (Element) mapper;
String resource = mapperElement.attributeValue("resource");
System.out.println("Mapper映射文件的路径:" + resource);
});
}
@Test
public void testParseSqlMapperXML() throws DocumentException {
SAXReader reader = new SAXReader();
InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("UserMapper.xml");
Document document = reader.read(inputStream);
// 获取namespace
String xpath = "/mapper";
Element mapper = (Element) document.selectSingleNode(xpath);
String namespace = mapper.attributeValue("namespace");
System.out.println("namespace = " + namespace);
// 获取mapper节点下所有子节点
List<Element> elements = mapper.elements();
elements.forEach(element -> {
// 获取SQLId
String id = element.attributeValue("id");
System.out.println("id = " + id);
// 获取resultType,没有这个属性的话,会自动返沪"null"
String resultType = element.attributeValue("resultType");
System.out.println("resultType = " + resultType);
// 获取标签中的SQL语句(表示获取标签中的文本内容,而且去除前后空白)
String sql = element.getTextTrim();
String newSQL = sql.replaceAll("#\\{[0-9A-Za-z_$]*}","?");
System.out.println(newSQL);
});
}
}
二、MyBatis框架的实现
2.1、引入依赖
<?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>
<groupId>star.light</groupId>
<artifactId>mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- 依赖 -->
<dependencies>
<!-- dom4j依赖 -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<!-- jaxen依赖 -->
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
</project>
2.2、创建核心类
SqlSessionFactoryBuilder:
package star.light.frame.core;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import star.light.frame.core.constant.Const;
import star.light.frame.core.datasource.JndiDataSource;
import star.light.frame.core.datasource.PoolDataSource;
import star.light.frame.core.datasource.UnPooledDataSource;
import star.light.frame.core.mapper.MappedStatement;
import star.light.frame.core.transaction.Transaction;
import star.light.frame.core.transaction.impl.JdbcTransaction;
import star.light.frame.core.transaction.impl.ManagedTransaction;
import star.light.frame.util.Resources;
import javax.sql.DataSource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* SqlSessionFactory构建其对象
* 通过SqlSessionFactoryBuilder的build()来解析核心配置文件,创建SqlSessionFactory对象
*/
public class SqlSessionFactoryBuilder {
public SqlSessionFactoryBuilder() {
}
/**
* 解析核心配置文件,来构建SqlSessionFactory对象
* @param inputStream 指向核心配置文件的一个输入流
* @return SqlSessionFactory对象
*/
public SqlSessionFactory build(InputStream inputStream){
SqlSessionFactory sqlSessionFactory = null;
try {
// 解析核心配置文件
SAXReader reader = new SAXReader();
// 读XML文件,但会Document对象,Document对象是文档对象,代表了整个XML文件
Document document = reader.read(inputStream);
// 从根找<configuration>标签,然后找<configuration>标签下的子标签<environments>标签
Element environments = (Element) document.selectSingleNode("/configuration/environments");
// 获取default默认环境id
String defaultId = environments.attributeValue("default");
// 获取默认的环境
Element environment = (Element) document.selectSingleNode("/configuration/environments/environment[@id='" + defaultId + "']");
// 获取事务管理器节点
Element transactionManagerElement = environment.element("transactionManager");
// 获取数据源节点
Element dataSourceElement = environment.element("dataSource");
// 存放SqlMapper.xml文件的路径的集合
List<String> sqlMapperXmlPathList = new ArrayList<>();
// 获取整个配置文件中所有mapper标签
List<Node> nodes = document.selectNodes("//mapper");
nodes.forEach(node -> {
Element mapper = (Element) node;
String resource = mapper.attributeValue("resource");
sqlMapperXmlPathList.add(resource);
});
// 获取数据源对象
DataSource dataSource = getDataSource(dataSourceElement);
// 获取事务管理器对象
Transaction transaction = getTransaction(transactionManagerElement,dataSource);
// 获取mappedStatements
Map<String, MappedStatement> mappedStatement = getMappedStatements(sqlMapperXmlPathList);
// 解析完成之后,构建SqlSessionFactory对象
sqlSessionFactory = new SqlSessionFactory(transaction,mappedStatement);
} catch (Exception e) {
e.printStackTrace();
}
return sqlSessionFactory;
}
/**
* 获取数据源对象
* @param dataSourceElement 数据源标签元素
* @return
*/
private DataSource getDataSource(Element dataSourceElement) {
DataSource dataSource = null;
String type = dataSourceElement.attributeValue("type").trim().toUpperCase();
Map<String,String> map = new HashMap<>();
// 获取所有的property
List<Element> propertyElements = dataSourceElement.elements("property");
propertyElements.forEach(propertyElement -> {
String name = propertyElement.attributeValue("name");
String value = propertyElement.attributeValue("value");
map.put(name,value);
});
if (Const.UN_POOLED_DATASOURCE.equals(type)) {
dataSource = new UnPooledDataSource(map.get("driver"),map.get("url"),map.get("username"),map.get("password"));
}
if (Const.POOLED_DATASOURCE.equals(type)) {
dataSource = new PoolDataSource();
}
if (Const.JNDI_DATASOURCE.equals(type)) {
dataSource = new JndiDataSource();
}
return dataSource;
}
/**
* 获取事务管理器对象
* @param transactionManagerElement 事务管理器标签元素
* @param dataSource 数据源对象
* @return
*/
private Transaction getTransaction(Element transactionManagerElement, DataSource dataSource) {
String type = transactionManagerElement.attributeValue("type").trim().toUpperCase();
Transaction transaction = null;
if (Const.JDBC_TRANSACTION.equals(type)) {
// 默认开启事务,将来是要手动提交
transaction = new JdbcTransaction(dataSource,false);
}
if (Const.MANAGED_TRANSACTION.equals(type)) {
transaction = new ManagedTransaction();
}
return transaction;
}
/**
* 解析所有的SqlMapper.xml配置文件,构造Map集合
* @param sqlMapperXmlPathList
* @return
*/
private Map<String, MappedStatement> getMappedStatements(List<String> sqlMapperXmlPathList) {
Map<String,MappedStatement> mappedStatementMap = new HashMap<>();
sqlMapperXmlPathList.forEach(sqlMapperXmlPath -> {
try {
SAXReader reader = new SAXReader();
// SqlMapper配置文件
Document document = reader.read(Resources.getResourceAsStream(sqlMapperXmlPath));
Element mapper = (Element) document.selectSingleNode("mapper");
// 获取命名空间
String namespace = mapper.attributeValue("namespace");
// 获取所有的子节点
List<Element> elements = mapper.elements();
elements.forEach(element -> {
// 获取sql的id
String id = element.attributeValue("id");
String sqlId = namespace + "." + id;
// 获取resultType
String resultType = element.attributeValue("resultType");
// 获取SQL语句
String sql = element.getTextTrim();
MappedStatement mappedStatement = new MappedStatement(sql, resultType);
mappedStatementMap.put(sqlId,mappedStatement);
});
} catch (Exception e) {
e.printStackTrace();
}
});
return mappedStatementMap;
}
}
SqlSessionFactory:
package star.light.frame.core;
import star.light.frame.core.mapper.MappedStatement;
import star.light.frame.core.transaction.Transaction;
import java.util.Map;
/**
* 一个数据库一般对应一个SqlSessionFactory对象
* 通过SqlSessionFactory对象可以获取SalSession对象(开启会话)
* 一个SqlSessionFactory对象可以开启多个SqlSession会话
*/
public class SqlSessionFactory {
/**
* 事务管理器属性
* 事务管理器是可以灵活切换的
* SqlSessionFactory类中的事务管理器应该是面向接口编程的
* SqlSessionFactory类中应该有一个事务管理器接口
*/
private Transaction transaction;
// 存放SQL语句的Map集合,key是sqlId,value是对应的SQL标签信息对象
private Map<String, MappedStatement> mappedStatementMap;
public SqlSessionFactory() {
}
public SqlSessionFactory(Transaction transaction, Map<String, MappedStatement> mappedStatementMap) {
this.transaction = transaction;
this.mappedStatementMap = mappedStatementMap;
}
public Transaction getTransaction() {
return transaction;
}
public void setTransaction(Transaction transaction) {
this.transaction = transaction;
}
public Map<String, MappedStatement> getMappedStatementMap() {
return mappedStatementMap;
}
public void setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) {
this.mappedStatementMap = mappedStatementMap;
}
/**
* 获取Session会话对象
* @return
*/
public SqlSession openSession(){
// 开启会话的前提是开启连接
transaction.openConnection();
// 创建SqlSession对象
SqlSession sqlSession = new SqlSession(this);
return sqlSession;
}
}
SqlSession:
package star.light.frame.core;
import star.light.frame.core.mapper.MappedStatement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
/**
* 专门负责执行SQL语句的会话对象
*/
public class SqlSession {
private SqlSessionFactory sqlSessionFactory;
public SqlSession(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
/**
* 执行insert语句向数据库表中插入记录
* @param sqlId SQL语句的id
* @param pojo 插入的数据
* @return
*/
public int insert(String sqlId,Object pojo){
int affectedRowCount = 0;
try {
// JDBC代码,执行insert语句,完成插入
Connection connection = sqlSessionFactory.getTransaction().getConnection();
// 获取映射文件中的SQL语句,insert into 表名(字段名1,字段名2,...) values(#{字段值1},#{字段值2},...)
String frameSql = sqlSessionFactory.getMappedStatementMap().get(sqlId).getSql();
// 替换后的SQL语句,insert into 表名(字段名1,字段名2,...) values(?,?,...)
String sql = frameSql.replaceAll("#\\{[a-zA-Z0-9_$]*}","?");
// 编译SQL语句
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 给占位符传值
// 不知道占位符的个数
// 不知道该将pojo对象中的哪个属性赋值给哪个占位符
// 不知道pojo对象属性的类型
int fromIndex = 0;
int index = 1; // 占位符的下标
while (true){
int targetStartIndex = frameSql.indexOf("#",fromIndex);
if (targetStartIndex < 0){
break;
}
int targetEndIndex = frameSql.indexOf("}",targetStartIndex);
String propertyName = frameSql.substring(targetStartIndex+2,targetEndIndex).trim();
fromIndex = targetEndIndex + 1;
// 有属性名id,调用getId()获取id的属性值
String getMethodName = "get" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
Method getMethod = pojo.getClass().getDeclaredMethod(getMethodName);
Object propertyValue = getMethod.invoke(pojo);
preparedStatement.setObject(index,propertyValue);
index++;
}
// 执行SQL语句
affectedRowCount = preparedStatement.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
return affectedRowCount;
}
/**
* 执行查询语句返回一个对象,该方法只适合返回一条记录的SQL语句
* @param sqlId SQL语句的id
* @param param
* @return
*/
public Object selectOne(String sqlId,Object param){
Object object = null;
try {
// 获取数据库连接
Connection connection = sqlSessionFactory.getTransaction().getConnection();
MappedStatement mappedStatement = sqlSessionFactory.getMappedStatementMap().get(sqlId);
// 获取sql语句 select 字段名1,字段名2,... from 表名 where 字段名 = ${字段值}
String frameSql = mappedStatement.getSql();
// 替换后的SQL语句,select 字段名1,字段名2,... from 表名 where 字段名 = ?
String sql = frameSql.replaceAll("#\\{[a-zA-Z0-9_$]*}","?");
// 编译SQL语句
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 给占位符传值
preparedStatement.setObject(1,param);
// 执行查询,返回结果集
ResultSet resultSet = preparedStatement.executeQuery();
// 要封装的结果类型
String resultType = mappedStatement.getResultType();
// 从结果集中取数据封装java对象
if (resultSet.next()) {
// 获取resultType的Class
Class<?> resultTypeClass = Class.forName(resultType);
// 调用无参的构造方法创建对象
object = resultTypeClass.getConstructor().newInstance();
// 给属性赋值
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 0; i < columnCount; i++) {
String propertyName = metaData.getColumnLabel(i + 1);
Object propertyValue = resultSet.getObject(propertyName);
Field field = resultTypeClass.getDeclaredField(propertyName);
field.setAccessible(true);
field.set(object, propertyValue);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return object;
}
public void commit(){
sqlSessionFactory.getTransaction().commit();
}
public void rollback(){
sqlSessionFactory.getTransaction().rollback();
}
public void close(){
sqlSessionFactory.getTransaction().close();
}
}
2.3、数据源的实现类
UNPOOLED 类型的数据源:
package star.light.frame.core.datasource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
/**
* 数据源的实现类:UNPOOLED
* 不使用连接池每一都新建连接对象
*/
public class UnPooledDataSource implements javax.sql.DataSource {
private String url;
private String username;
private String password;
/**
* 创建一个数据源对象
* @param driver
* @param url
* @param username
* @param password
*/
public UnPooledDataSource(String driver, String url, String username, String password) {
try {
// 直接注册驱动
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
this.url = url;
this.username = username;
this.password = password;
}
@Override
public Connection getConnection() throws SQLException {
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
POOLED 类型的数据源:
package star.light.frame.core.datasource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
/**
* 数据源的实现类:POOLED
* 使用框架内置的数据库连接池来获取Connection对象
* 这里就不实现了
*/
public class PoolDataSource implements javax.sql.DataSource {
@Override
public Connection getConnection() throws SQLException {
// 从数据库连接池中获取对象
return null;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
JNDI 类型的数据源
package star.light.frame.core.datasource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
/**
* 数据源的实现类:JNDI
* 使用第三方的数据库连接池获取Connection对象
* 这里就不实现了
*/
public class JndiDataSource implements javax.sql.DataSource {
@Override
public Connection getConnection() throws SQLException {
return null;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
2.4、事务管理器
事务管理器接口:
package star.light.frame.core.transaction;
import java.sql.Connection;
/**
* 事务管理器接口
* 所有的事务管理器都应该遵循该规范
* JDBC事务管理器、MANAGED事务管理器都应该实现这个接口
* Transaction事务管理器提供控制事务的方法
*/
public interface Transaction {
void commit(); // 提交事务
void rollback(); // 回滚事务
void close(); // 关闭事务
void openConnection(); // 开启数据库连接
Connection getConnection(); // 获取数据库连接对象
}
JDBC事务管理器:
package star.light.frame.core.transaction.impl;
import star.light.frame.core.transaction.Transaction;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* JDBC事务管理器
*/
public class JdbcTransaction implements Transaction {
/**
* 数据源属性
* 面向接口编程
*/
private DataSource dataSource;
/**
* 自动提交标记
* true表示自动提交
* false表示不采用自动提交
*/
private boolean autoCommit;
// 连接对象
private Connection connection;
/**
* 创建事务管理器对象
* @param dataSource
* @param autoCommit
*/
public JdbcTransaction(DataSource dataSource, boolean autoCommit) {
this.dataSource = dataSource;
this.autoCommit = autoCommit;
}
@Override
public void openConnection(){
if (connection == null) {
try {
connection = dataSource.getConnection();
// 开启事务
connection.setAutoCommit(autoCommit);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@Override
public Connection getConnection() {
return connection;
}
@Override
public void commit() {
try {
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void rollback() {
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void close() {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
MANAGED事务管理器:
package star.light.frame.core.transaction.impl;
import star.light.frame.core.transaction.Transaction;
import java.sql.Connection;
/**
* MANAGED事务管理器
* 这里就不实现了
*/
public class ManagedTransaction implements Transaction {
@Override
public void openConnection() {
}
@Override
public Connection getConnection() {
return null;
}
@Override
public void commit() {
}
@Override
public void rollback() {
}
@Override
public void close() {
}
}
2.5、SQL标签的类
package star.light.frame.core.mapper;
/**
* 普通的Java类,封装了一个SQL标签
* 一个MappedStatement对象对应一个SQL标签
* 一个SQL标签的所有信息封装到MappedStatement对象中
*/
public class MappedStatement {
// SQL语句
private String sql;
/**
* 要封装的结果集类型
* insert、delete、update语句的时候,resultType是null
* 只有当sql语句是select语句的时候resultType才有值
*/
private String resultType;
public MappedStatement() {
}
public MappedStatement(String sql, String resultType) {
this.sql = sql;
this.resultType = resultType;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
@Override
public String toString() {
return "MappedStatement{" +
"sql='" + sql + '\'' +
", resultType='" + resultType + '\'' +
'}';
}
}
2.6、常量类
package star.light.frame.core.constant;
/**
* 整个框架的常量类
*/
public class Const {
public static final String UN_POOLED_DATASOURCE = "UNPOOLED";
public static final String POOLED_DATASOURCE = "POOLED";
public static final String JNDI_DATASOURCE = "JNDI";
public static final String JDBC_TRANSACTION = "JDBC";
public static final String MANAGED_TRANSACTION = "MANAGED";
}
2.7、工具类
package star.light.frame.util;
import java.io.InputStream;
/**
* 工具类专门完成“类路径”的加载
*/
public class Resources {
/**
* 工具类的构造方法都是建议私有化的
* 因为工具类中的方法都是静态的,不需要创建对象就行调用
* 为了避免new对象,建议所有构造方法私有化
*/
public Resources() {}
/**
* 从路径中加载资源
* @param inputstream 放在类路径当中的资源文件
* @return 指向资源文件的一个输入流
*/
public static InputStream getResourceAsStream(String inputstream){
return ClassLoader.getSystemClassLoader().getResourceAsStream(inputstream);
}
}
2.8、打包
三、框架的测试
3.1、创建数据库
CREATE DATABASE IF NOT EXISTS db_test;
USE db_test;
CREATE TABLE IF NOT EXISTS t_user(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(20),
password VARCHAR(20),
age INT,
sex VARCHAR(10),
email VARCHAR(30)
);
3.2、引入依赖
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!-- junit依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
3.3、创建核心配置文件
核心配置文件的取命随意,这里取命为 config.xml,存放在类的根目录下;
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!-- environments:配置多个连接数据库环境 -->
<environments default="development">
<!-- environment:配置某个具体的环境 -->
<environment id="development">
<!-- 设置事务管理方式 -->
<transactionManager type="JDBC"/>
<!-- 配置数据源 -->
<dataSource type="UNPOOLED">
<!-- 设置连接数据库的驱动 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!-- 设置连接数据库的地址 -->
<property name="url" value="jdbc:mysql://localhost:3306/db_test"/>
<!-- 设置连接数据库的用户名 -->
<property name="username" value="root"/>
<!-- 设置连接数据库的密码 -->
<property name="password" value="27185.Sakura"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<!--
指定 XxxMapper.xml 配置文件的路径
resource属性自动会从类的根路径下开始查找资源
url属性从绝对路径当中加载资源,一般很少使用,语法格式如下:file:///绝对路径
-->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
3.4、创建实体类
package star.light.pojo;
public class User {
// 数据库表当中的字段应该和pojo类的属性一一对应
// 建议使用包装类,这样可以防止从数据库查出字段的值为null的问题
private Integer id;
private String username;
private String password;
private Integer age;
private String sex;
private String email;
public User() {
}
public User(Integer id, String username, String password, Integer age, String sex, String email) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
this.sex = sex;
this.email = email;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", email='" + email + '\'' +
'}';
}
}
3.5、创建映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="userMapper">
<!-- 添加一条用户信息 -->
<insert id="insertUser">
insert into t_user(id,username,password,age,sex,email)
values (#{id},#{username},#{password},#{age},#{sex},#{email})
</insert>
<!-- 根据id查询用户信息 -->
<select id="selectUserById" resultType="star.light.pojo.User">
select id,username,password,age,sex,email from t_user where id = #{id}
</select>
</mapper>
3.6、测试程序
package star.light.test;
import org.junit.Test;
import star.light.frame.core.SqlSession;
import star.light.frame.core.SqlSessionFactory;
import star.light.frame.core.SqlSessionFactoryBuilder;
import star.light.frame.util.Resources;
import star.light.pojo.User;
public class MyFrameTest {
@Test
public void testInsertUser(){
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User(null,"Sakura","Sakura",10,"女","[email protected]");
int affectedRowCount = sqlSession.insert("userMapper.insertUser", user);
System.out.println("affectedRowCount = " + affectedRowCount);
sqlSession.commit();
sqlSession.close();
}
@Test
public void testSelectById(){
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
Object object = sqlSession.selectOne("userMapper.selectUserById", 1);
System.out.println(object);
}
}
标签:return,String,05,id,sql,MyBatis,import,public,底层
From: https://www.cnblogs.com/nanoha/p/16770053.html