mybatis框架作为一个流行的和数据库打交道的持久层框架,在我们实际开发中运用广泛,其框架的设计思想值得我们这些新手学习。笔者近几天开始学习mybatis框架,便自己参照mybatis框架的源码以及其它一些相关文献,自定义实现一个简单的mybatis框架。下面便开始进入正题:
1.我们在使用mybatis框架的时候,一般使用以下几个步骤:
//读取配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//实例化SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
//产生一个SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//利用SqlSession创建UserDAao的代理对象
UserDao userDao = sqlSession.getMapper(UserDao.class);
//利用代理对象执行方法
List<User> userList = userDao.getAll();
//关闭资源
sqlSession.close();
此次就参考mybatis的使用步骤,我们不导入mybatis的依赖包,逐个自定义实现该功能所需要的类。
2.刚开始如图所示:
这些必要的类报错,我们需要自己手动定义创建。
2.1首先创建Resources类。并且创建一个static的getResourceAsStream(String filePath)方法。
2.2其次创建SqlSessionFactoryBuilder类,并且创建一个build(InputStream inputStream)方法
2.3再创建SqlSessionFactory接口(根据mybatis的源码可以知道这个是接口),然后添加一个openSession()方法。
2.4最后创建SqlSession接口(根据mybatis的源码可以知道这个是接口)。然后创建一个getMapper(Class daoClass)和close()方法。
虽然这些类和方法还没有真正实现,但是此时发现已经不报错了。
3.在resources资源根目录中创建配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--mybatis的主配置文件-->
<configuration>
<environments default="development">
<environment id="development">
<!-- 配置事务类型 -->
<transactionManager type="JDBC"/>
<!-- 配置连接信息 -->
<dataSource type="POOLED">
<!-- 配置加载MySQL的驱动 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!-- MySQL连接的数据库地址,笔者这里是在本地localhost上测试,并且数据库的名称为mybatisdemo -->
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdemo?serverTimezone=UTC"/>
<!-- MySQL连接的用户名 -->
<property name="username" value="root"/>
<!-- MySQL的连接密码 -->
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 指定配置文件的的位置,映射文件指的是每个dao的位置-->
<mappers>
<!-- 指定Userdao的位置,使用XML配置时使用resource标签-->
<!--使用注解配置的时候使用class标签,使用UserDao的全限定类名 -->
<mapper resource="dao/UserDao.xml"></mapper>
</mappers>
</configuration>
4.首先实现Resources类的getResourceAsStream方法,其具体代码为:
/**
* @Description :根据传入的资源文件名获取对应的输入流
* @Date 15:17 2020/7/25 0025
* @Param * @param filePath :资源文件名,比如 demo.txt
* @return java.io.InputStream
**/
public static InputStream getResourceAsStream(String filePath) {
return Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
}
5.接着实现SqlSessionFactoryBuilder的build方法,其具体代码为:
/**
* @Description :根据配置文件输入流加载配置信息并封装到一个对象中去
* @Date 1:36 2020/7/26 0026
* @Param * @param inputStream :配置文件输入流
* @return mybatis.SqlSessionFactory
**/
public SqlSessionFactory build(InputStream inputStream) {
//调用工具类加载配置文件并将配置信息封装到一个Configuration类中
Configurantion configurantion = MybatisUtil.LoaderConfiguration(inputStream);
//利用SqlSessionFactory接口的一个实现类
return new DefaultSqlSessionFactory(configurantion);
}
在这个类中我们发现需要对配置信息进行封装,将数据库连接中的driver,url,userName,password等信息封装到一个对象Configuration中去:
package mybatis;
import java.util.HashMap;
import java.util.Map;
/**
* Classname:Configurantion
*
* @description:数据库连接配置信息类
* @author: 陌意随影
* @Date: 2020-07-25 15:40
* @Version: 1.0
**/
public class Configurantion {
//连接驱动
private String driver;
//连接用户名
private String username;
//密码
private String password;
//连接URL
private String url;
//dao接口的关键配置信息map
private Map<String,Mapper> mapperMap = new HashMap<>();
public Map<String, Mapper> getMapperMap() {
return mapperMap;
}
public void setMapperMap(Map<String, Mapper> mapperMap) {
this.mapperMap.putAll(mapperMap);
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
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 String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return "Configurantion{" +
"driver='" + driver + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
", url='" + url + '\'' +
'}';
}
}
然后我们继续创建一个工具类MybatisUtil,并添加一个static方法LoaderConfiguration用于加载配置信息并封装到Configuration对象中然后返回:
/**
* @Description :通过资源文件流加载配置信息
* @Date 15:45 2020/7/25 0025
* @Param * @param inputStream :
* @return mybatis.Configurantion
**/
public static Map<String, Mapper> XMLLoader(String mapperPath) throws DocumentException {
InputStream resourceAsStream = Resources.getResourceAsStream(mapperPath);
//获取读取流对象
SAXReader reader = new SAXReader();
//获取document对象
Document document = reader.read(resourceAsStream);
//获取根节点,根节点就是mapper节点
Element rootElement = document.getRootElement();
//获取mapper的namespace的属性值
String namespace = rootElement.attributeValue("namespace");
//获取mapper下的每个select节点
List nodes = rootElement.selectNodes("//select");
Map<String, Mapper> mapperMap = new HashMap<>();
if (nodes!=null && nodes.size() != 0){
Map<String, Mapper> mappers = getMapperMap(namespace, nodes,Mapper.SELECT);
mapperMap.putAll(mappers);
}
//获取mapper下的每个insert节点
nodes = rootElement.selectNodes("//insert");
if (nodes!=null && nodes.size() != 0){
Map<String, Mapper> mappers = getMapperMap(namespace, nodes,Mapper.INSERT);
mapperMap.putAll(mappers);
}
//获取mapper下的每个delete节点
nodes = rootElement.selectNodes("//delete");
if (nodes!=null && nodes.size() != 0){
Map<String, Mapper> mappers = getMapperMap(namespace, nodes,Mapper.DELETE);
mapperMap.putAll(mappers);
}
//获取mapper下的每个update节点
nodes = rootElement.selectNodes("//update");
if (nodes!=null && nodes.size() != 0){
Map<String, Mapper> mappers = getMapperMap(namespace, nodes,Mapper.UPDATE);
mapperMap.putAll(mappers);
}
return mapperMap;
}
/**
* @Description :根据namespace,操作类型节点以及操作类型获取封装的Map
* @Date 20:19 2020/7/25 0025
* @Param * @param namespace: dao接口的全限定类名
* @param nodes :操作类型的节点集合
* @param sqlType :操作类型
* @return java.util.Map<java.lang.String,mybatis.Mapper>
**/
private static Map<String, Mapper> getMapperMap(String namespace, List nodes,int sqlType) {
Map<String,Mapper> mapperMap = new HashMap<>();
for (Object o:nodes){
Element element = (Element) o;
//获取select的id属性值,相当于dao接口的方法名
String daoMethodName = element.attributeValue("id");
//获取select的resultType的属性值,相当于dao接口的返回值类型
String resultType = element.attributeValue("resultType");
//组建一个dao全限定类名和方法名组成的key,比如 dao.UserDao.getAll,标识dao包下的USerDao接口中的getAll方法
String key = namespace+"."+daoMethodName;
//获取select标签的sql语句
String sql = element.getText();
Mapper mapper = new Mapper();
//设置返回值类型
mapper.setResultTypePath(resultType);
//设置sql语句
mapper.setSql(sql);
//将mapper存入Map中
mapperMap.put(key,mapper);
//设置sql操作类型
mapper.setSqlType(sqlType);
}
return mapperMap;
}
/**
* @Description :加载XML的配置信息
* @Date 15:45 2020/7/25 0025
* @Param * @param inputStream :资源文件流
* @return mybatis.Configurantion
**/
public static Configurantion LoaderConfiguration(InputStream inputStream) {
//获取读取流对象
SAXReader reader = new SAXReader();
Configurantion configurantion = new Configurantion();
try {
//根据资源文件流获取document对象
Document document = reader.read(inputStream);
//获取文档的根节点
Element rootElm = document.getRootElement();
//获取默认的数据库配置
List list = rootElm.selectNodes("environments");
if (list == null || list.size() ==0){
throw new RuntimeException("尚未配置数据库连接环境信息!");
}
//获取environments的id
Element environmentsEle = (Element) list.get(0);
String defaultEnvironmentId = environmentsEle.attributeValue("default");
//获取所有的已经配置的数据库环境
List environmentNodes = environmentsEle.elements();
if (environmentNodes == null || environmentNodes.size() ==0){
throw new RuntimeException("尚未配置数据库连接环境信息!");
}
//获取与默认的environments的id相同的environment节点
List propertyList = null;
for (Object o: environmentNodes){
Element environmentEle = (Element) o;
//找到默认的数据库连接环境
if (defaultEnvironmentId.equals(environmentEle.attributeValue("id"))){
//获取数据库的链接信息,driver,username,password,url
//获取该环境下的property标签
propertyList = environmentEle.selectNodes("//property");
//跳出循环
break;
}
}
if(propertyList == null){
throw new RuntimeException("尚未配置数据库连接环境信息!");
}
for (Object p:propertyList ) {
Element propertyEle = (Element) p;
//获取property标签的name属性值
String name = propertyEle.attributeValue("name");
if ("url".equals(name)){
//设置URL的值
configurantion.setUrl(propertyEle.attributeValue("value"));
}
if ("driver".equals(name)){
//设置driver的值
configurantion.setDriver(propertyEle.attributeValue("value"));
}
if ("username".equals(name)){
//设置username的值
configurantion.setUsername(propertyEle.attributeValue("value"));
}
if ("password".equals(name)){
//设置password的值
configurantion.setPassword(propertyEle.attributeValue("value"));
}
}
//定位到mybatis配置文件中的 mappers 中的mapper标签的所有节点
List mapperNodes = rootElm.selectNodes("mappers/mapper");
if (mapperNodes == null || mapperNodes.size() == 0){
throw new RuntimeException("您未配置mapper的信息!");
}
//逐个遍历mapper节点然后获取其中resource的值
for (Object e:mapperNodes){
Element element = (Element) e;
String daoPath = element.attributeValue("resource");
if (daoPath != null){
//是XML配置
Map<String, Mapper> mapperMap = XMLLoader(daoPath);
configurantion.setMapperMap(mapperMap);
}else{
//是注解配置
}
}
} catch (DocumentException e) {
e.printStackTrace();
}
return configurantion;
}
在进行解析配置文件时,使用了dom4j技术进行解析xml文件。在进行解析的过程中,考虑到mappers的配置有xml配置和注解配置,所以需要分开来,此次首先考虑的是继续xml的配置。为了我们实现功能,我们需要从dao.xml配置文件中获取接口的全限定方法名以及返类型的全限定类名,然后封装到一个mapper中,比如UserDao中的getAll()方法,那么其全限定方法名为“dao.UserDao.getAll”,表示包dao下的接口UserDao中的getAll方法,“entity.User”表示entity包下的User类,每一个方法对应一条Sql语句,同时对应一个mapper,所以一个Configuration中需要有多个mapper,可以使用Map来存取,它的key就是接口的全限定方法名,value就是对应的mapper,其Mapper类的具体代码为:
package mybatis;
/**
* Classname:Mapper
*
* @description:封装sql语句和返回值类型全限定类名
* @author: 陌意随影
* @Date: 2020-07-25 17:04
* @Version: 1.0
**/
public class Mapper {
//返回值了类型的类限定名
private String resultTypePath;
//sql语句
private String sql;
//sql操作类型
private int sqlType;
/**查询类型*/
public static int SELECT = 1;
/**插入类型*/
public static int INSERT = 2;
/**删除类型*/
public static int DELETE = 3;
/**更新类型*/
public static int UPDATE = 4;
public String getResultTypePath() {
return resultTypePath;
}
public int getSqlType() {
return sqlType;
}
public void setSqlType(int sqlType) {
this.sqlType = sqlType;
}
public void setResultTypePath(String resultTypePath) {
this.resultTypePath = resultTypePath;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
@Override
public String toString() {
return "Mapper{" +
"resultTypePath='" + resultTypePath + '\'' +
", sql='" + sql + '\'' +
", sqlType=" + sqlType +
'}';
}
}
然后将封装好的Mapper存入到Configuration中去。我们获取了Configuration后,通过创建一个接口SqlSessionFactory的一个实现类DefaultSqlSessionFactory进行返回一个SqlSessionFactory。
5.紧接着实现SqlSessionFactory接口中的openSession方法,实际上是在SqlSessionFactory的一个实现类DefaultSqlSessionFactory中进行实现,其代码为:
/**
* Classname:DefaultSqlSessionFactory
*
* @description:构建SQLSession的默认工厂
* @author: 陌意随影
* @Date: 2020-07-25 17:52
* @Version: 1.0
**/
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private Configurantion configurantion ;
public DefaultSqlSessionFactory(Configurantion configurantion){
this.configurantion = configurantion;
}
@Override
public SqlSession openSession() {
return new DefaultSqlSession(configurantion);
}
}
我们发现实际上在调用sqlSessionFactory.openSession();时,实际上是调用new DefaultSqlSession(configurantion)后获取一个SqlSession接口的实现类。
6.实现 sqlSession.getMapper(UserDao.class),该过程中使用了动态代理的方法。其代码为:
@Override
public <T> T getMapper(Class<T> daoClass) {
Object proxyInstance = Proxy.newProxyInstance(daoClass.getClassLoader(), new Class[]{daoClass}, new MapperProxy(configurantion.getMapperMap(), connection));
return (T) proxyInstance;
}
在该方法过程中,使用了动态代理进行加强需要被代理的对象。
动态代理底层实现
动态代理具体步骤:
6.1通过实现 InvocationHandler 接口创建自己的调用处理器;
6.2通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
6.3通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
6.4通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
newProxyInstance(ClassLoader loader,(被代理对象的类加载器)
Class[] interfaces,(被代理对像接口的字节码数组)
InvocationHandler h(由代理实例的调用处理程序实现的接口。)
)
为了方便,添加InvocationHandler 接口的一个实现类MapperProxy,其代码为:
package mybatis;
import utils.DefaultResultSetHandler;
import utils.MyBatisJDBCUtil;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Classname:MapperProxy
*
* @description:Mapper的代理对象类
* @author: 陌意随影
* @Date: 2020-07-25 18:06
* @Version: 1.0
**/
public class MapperProxy implements InvocationHandler {
private Map<String, Mapper> mapperMap;
private Connection connection;
public MapperProxy( Map<String, Mapper> mapperMap,Connection connection) {
this.mapperMap = mapperMap;
this.connection = connection;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取方法名
String methodName = method.getName();
//获取方法所在的类的名称
String className = method.getDeclaringClass().getName();
//组合成一个key
String key = className+"."+methodName;
//获取mapperMap中的的对应mapper
Mapper mapper = mapperMap.get(key);
//判断是否有对应的mapper
if (mapper == null){
throw new RuntimeException("传的参数有误!");
}
//执行查询语句返回结果
List<Object> objectList = MyBatisJDBCUtil.query(mapper, connection, new DefaultResultSetHandler(),args);
//获取返回类型
Class<?> returnType = method.getReturnType();
//获取集合的字节码用于判断返回类型是否是集合的子类
Class<Collection> collectionClass = Collection.class;
if (objectList!= null && objectList.size() != 0){
if (collectionClass.isAssignableFrom(returnType)){
//如果返回类型是集合则返回一个集合
return objectList;
}else{
//返回类型是一个对象则直接返回一个对象
return objectList.get(0);
}
}else{
return null;
}
}
}
Object invoke(Object proxy,Method method, Object[] args) throws Throwable处理代理实例上的方法调用并返回结果。 当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。
参数
proxy - 调用该方法的代理实例
method - 对应于在代理实例上调用的接口方法的方法实例。 方法对象的声明类将是方法声明的接口,它可以是代理类继承方法的代理接口的超级接口。
args - 包含代理实例上方法调用中传递的参数值的对象数组,如果接口方法不带参数, null 。 原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean
在进行对被代理对象的接口方法进行加强的时候,需要用到与数据库连接后进行操作的工具类MyBatisJDBCUtil,其具体代码为:
package utils;
import mybatis.Configurantion;
import mybatis.Mapper;
import java.sql.*;
import java.util.List;
/**
* Classname:MyBatisJDBCUtil
*
* @description:获取sql连接的工具类
* @author: 陌意随影
* @Date: 2020-07-25 18:17
* @Version: 1.0
**/
public class MyBatisJDBCUtil {
public static Connection getConnection(Configurantion configurantion) {
try {
//加载驱动
Class.forName(configurantion.getDriver());
try {
//获取连接
Connection connection = DriverManager.getConnection(configurantion.getUrl(), configurantion.getUsername(), configurantion.getPassword());
return connection;
} catch (SQLException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
public static <T> List<T> query(Mapper mapper, Connection connection, ResultSetHandler resultSetHandler, Object[] param) throws Exception {
//设置禁止自动提交
connection.setAutoCommit(false);
PreparedStatement preparedStatement = null;
List<T> t = null;
try {
//获取预处理对象
preparedStatement = connection.prepareStatement(mapper.getSql());
//设置占位参数
if (param != null && param.length != 0){
for (int i = 0; i < param.length;i++){
preparedStatement.setObject(i+1,param[i]);
}
}
//获取结果集
ResultSet resultSet = preparedStatement.executeQuery();
//对结果集进行处理
t = resultSetHandler.resultSet(resultSet,mapper.getResultTypePath());
}catch (Exception e){
e.printStackTrace();
}finally {
//设置允许自动提交
connection.setAutoCommit(true);
//返回处理结果
return t;
}
}
public static int update(String sql,Connection connection,String ...param) throws Exception {
//设置禁止自动提交
connection.setAutoCommit(false);
PreparedStatement preparedStatement = null;
int result = -1;
try {
//获取预处理对象
preparedStatement = connection.prepareStatement(sql);
//设置占位参数
if (param != null && param.length != 0){
for (int i = 0; i < param.length;i++){
preparedStatement.setObject(i+1,param[i]);
}
}
//获取结果集
result = preparedStatement.executeUpdate();
}catch (Exception e){
e.printStackTrace();
}finally {
//设置允许自动提交
connection.setAutoCommit(true);
//返回处理结果
return result;
}
}
}
在工具类MyBatisJDBCUtil中,使用到了ResultSetHandler接口用于处理数据库查询后获取的结果集Resultset,ResultSetHandler的一个子类实现DefaultResultSetHandler,其具体的代码为:
package utils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Classname:DefaultResultSetHandler
* @description:处理结果集
* @author: 陌意随影
* @Date: 2020-07-25 21:12
* @Version: 1.0
**/
public class DefaultResultSetHandler<T> implements ResultSetHandler {
/**
* @param resultTypePath :需要封装到的实体类
* @return java.util.List<T>
* @Description :处理并封装结果集对象到对应的实体类中去
* @Date 22:58 2020/7/25 0025
* @Param * @param resultSet :包含数据库中实体类表信息的结果集
**/
@Override
public List<T> resultSet(ResultSet resultSet, String resultTypePath) throws Exception {
//通过反射获取返回类型的字节码
Class<T> obj = (Class<T>) Class.forName(resultTypePath);
if (obj == null) {
throw new IllegalAccessException("封装类型有误!");
}
//获取构造器
Constructor<T> constructor = obj.getConstructor();
//获取所有的方法
Method[] objMethods = obj.getMethods();
List<T> list = new ArrayList<>();
while (resultSet.next()) {
//实例化一个实体类
T t = constructor.newInstance();
//获取实体类的set方法
for (Method method : objMethods) {
//获取方法的名字
String methodName = method.getName();
//找到set方法
if (methodName.startsWith("set")) {
//获取set方法对应的属性名称
String fieldName = methodName.substring(3);
//获取set方法的参数类型,set方法只有一个参数
Class<?> parameterType = method.getParameterTypes()[0];
//获取set方法参数类型
String parameterTypeName = parameterType.getName();
//根据属性名从result中的获取对应数据库中的值并填充到实体对象中国
if ("java.lang.String".equals(parameterTypeName)) {
method.invoke(t, resultSet.getString(fieldName));
} else if ("java.lang.Integer".equals(parameterTypeName)) {
method.invoke(t, resultSet.getInt(fieldName));
} else if ("java.lang.Double".equals(parameterTypeName)) {
method.invoke(t, resultSet.getDouble(fieldName));
} else if ("java.lang.Byte".equals(parameterTypeName)) {
method.invoke(t, resultSet.getByte(fieldName));
} else if ("java.lang.Float".equals(parameterTypeName)) {
method.invoke(t, resultSet.getFloat(fieldName));
} else if ("java.lang.Boolean".equals(parameterTypeName)) {
method.invoke(t, resultSet.getBoolean(fieldName));
} else if ("java.util.Date".equals(parameterTypeName)) {
//将日期转化为指定格式的字符串
String dateToStr = DateUtil.dateToStr(resultSet.getDate(fieldName));
//将指定的字符串转换为日期
Date date = DateUtil.strToDate(dateToStr);
method.invoke(t, date);
}
}
}
list.add(t);
}
return list;
}
}
7.我们要自己写一个dao接口,用于进行各种操作,其代码为:
package dao;
import entity.User;
import java.util.List;
/**
* Classname:mybatisdemo
* @description:User的dao接口
* @author: 陌意随影
* @Date: 2020-07-24 10:35
*/
public interface UserDao {
/**
* @date: 2020/7/24 0024 10:41
* @description:获取所有的用户信息
* @return: 返回包含所有的用户的list
*/
List<User> getAll();
/**
* @Description :根据id获取制定对象
* @Date 0:32 2020/7/26 0026
* @Param * @param id :
* @return entity.User
**/
User getOne(int id);
}
通过 UserDao userDao = sqlSession.getMapper(UserDao.class);我们可以获取一个经过动态代理模式加强后的UserDao接口类,当调用UserDao的方法时,会相应自动调用MapperProxy中的invoke方法,从而UserDao的方法会相应得到加强,即使不用自己手动UserDao接口的实现类,也能够完成我们想要的功能。
8.在resources中创建一个包dao,然后在包dao中创建一个UserDao.xml,其代码为:
<?xml version="1.0" encoding="UTF-8" ?>
<!--指定UserDao接口的全限定类名-->
<mapper namespace="dao.UserDao">
<!-- id即为UserDao接口的getAll()函数的方法名-->
<select id="getAll" resultType="entity.User">
select * from User
</select>
<select id ="getOne" resultType="entity.User">
select *from User where id=?;
</select>
</mapper>
9.到此自定义的mybatis框架基本完成,下面进行测试:
package dao;
import entity.User;
import mybatis.Resources;
import mybatis.SqlSession;
import mybatis.SqlSessionFactory;
import mybatis.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;
import java.io.InputStream;
import java.util.List;
/**
* Classname:UserDaoTest
*
* @description:测试Userdao
* @author: 陌意随影
* @Date: 2020-07-24 10:49
* @Version: 1.0
**/
public class UserDaoTest {
@Test
public void testGetUserAll(){
try {
//读取配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//实例化SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
//产生一个SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//利用SqlSession创建UserDAao的代理对象
UserDao userDao = sqlSession.getMapper(UserDao.class);
//利用代理对象执行方法
List<User> userList = userDao.getAll();
for (User u:userList ) {
System.out.println(u);
}
User user = userDao.getOne(1);
//关闭资源
sqlSession.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
10.项目的目录为:
11.本项目的数据库MySQL文件User表为:
CREATE TABLE `user` (
`id` int(4) NOT NULL AUTO_INCREMENT,
`name` varchar(16) NOT NULL,
`password` varchar(16) NOT NULL,
`age` int(3) DEFAULT NULL,
`createtime` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
12本项目源码已经打包,有需要请直接点击下载(下载解压后直接导入idea即可):http://moyisuiying.com/wp-content/uploads/2020/07/mybatis-self.rar