# 手写Mybatis框架 Mybatis框架简单实现 Mybatis原理

tech2022-08-10  115

简易版Mybatis框架

自己的Mybatis框架设计图

依照Mybatis的框架执行原理,实现简易版的Mybatis框架
数据库建User表:
建立如下的实体表,用于增删改查 CREATE TABLE `NewTable` ( `id` int(20) NOT NULL AUTO_INCREMENT , `username` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL , `password` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL , `gender` int(1) NULL DEFAULT NULL , `age` int(2) NULL DEFAULT NULL , `idcard` int(20) NULL DEFAULT NULL , `phone` int(15) NULL DEFAULT NULL , PRIMARY KEY (`id`) );
新建Maven项目引入需要的依赖
引入Mysql驱动依赖、Dom4j(解析Xml)、FastJson(Json工具) <dependencies> <!-- Mysql 驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!-- xml解析工具包 --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.1</version> </dependency> <!-- Json工具--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> </dependency> </dependencies>
数据源配置文件:mysqlconfig.xml
<?xml version="1.0" encoding="UTF-8" ?> <database> <property name="driverClassName">com.mysql.jdbc.Driver</property> <property name="url">jdbc:mysql://localhost:3306/test</property> <property name="username">root</property> <property name="password">root</property> </database>
连接数据库和解析Mapper的工具类:MyConfiguration
public class MyConfiguration { private ClassLoader classLoader=ClassLoader.getSystemClassLoader(); private Logger logger= LoggerFactory.getLogger(MyConfiguration.class); /** * 读取数据库的Xml信息 */ public Connection getDataSourceInfo(){ try{ InputStream inputStream=classLoader.getResourceAsStream("mysqlconfig.xml"); SAXReader reader=new SAXReader(); Document document=reader.read(inputStream); Element element=document.getRootElement(); logger.info("==========数据库配置信息读取成功!"); return getConnection(element); }catch (Exception e){ logger.error(e.getMessage()); return null; } } /** * 得到XMl中的数据库的配置信息返回连接 */ private Connection getConnection(Element element) { String driverClassName=null; String url=null; String username=null; String password=null; if(!"database".equals(element.getName())){ throw new RuntimeException("数据库xml配置读取错误!"); } List<Object> property = element.elements("property"); for (Object o : property) { Element el= (Element) o; String name = el.attribute(0).getValue(); String value= (String) el.getData(); if(name==null || value==null){ throw new RuntimeException("property信息读取失败!"); } switch (name){ case "driverClassName": driverClassName=value; break; case "url": url=value; break; case "username": username=value; break; case "password": password=value; break; default: throw new RuntimeException("==========数据库信息有错误!"); } } try { Connection connection=null; Class.forName(driverClassName); logger.info("==========连接数据库获取驱动成功!"); connection= DriverManager.getConnection(url,username,password); logger.info("==========连接数据库成功!"); return connection; }catch (Exception e){ logger.error(e.getMessage()); return null; } } /** * 读取Mapper和xml中的配置信息 */ public MapperBean readMapper(String path){ MapperBean mapperBean=new MapperBean(); try{ InputStream inputStream=classLoader.getResourceAsStream(path); SAXReader saxReader=new SAXReader(); Document document=saxReader.read(inputStream); Element root=document.getRootElement(); // 存储xml文件中namespace的值 mapperBean.setInterfaceName(root.attributeValue("namespace").trim()); // 存储方法 List<Function> list=new ArrayList<>(); for(Iterator iterator=root.elementIterator();iterator.hasNext();){ Function function=new Function(); Element element= (Element) iterator.next(); String sqlType=element.getName().trim(); String functionNam=element.attributeValue("id").trim(); String sql=element.getText().trim(); String resultType=null; Object newinstance=null; if(null!=element.attributeValue("resultType")){ resultType=element.attributeValue("resultType"); try{ newinstance=Class.forName(resultType).newInstance(); }catch (Exception e){ logger.error(e.getMessage()); } function.setResultType(resultType); } function.setFunctionName(functionNam); function.setSqlType(sqlType); function.setSql(sql); list.add(function); } // 所有的mapper.xml文件解析后保存 mapperBean.setList(list); }catch (Exception e){ return null; } return mapperBean; } }
存放Mapper类信息的实体类:MapperBean、省略setget方法
public class MapperBean { // 接口名 private String interfaceName; // 接口下的所有方法 private List<Function> list; public MapperBean(){} public MapperBean(String interfaceName, List<Function> list) { this.interfaceName = interfaceName; this.list = list; } @Override public String toString() { return "MapperBean{" + "interfaceName='" + interfaceName + '\'' + ", list=" + list + '}'; } }
存放Mapper.xml的信息的实体类:Function
public class Function { private String sqlType; private String functionName; private String sql; private String resultType; private String parameterType; public Function(){} public Function(String sqlType, String functionName, String sql, String resultType, String parameterType) { this.sqlType = sqlType; this.functionName = functionName; this.sql = sql; this.resultType = resultType; this.parameterType = parameterType; } }
MyMapperProxy代理类完成读取的UserMapper.xml中方法与真实方法校验,并调用执行对应的方法
public class MyMapperProxy implements InvocationHandler { private MysqlSession mysqlSession; private MyConfiguration myConfiguration; public MyMapperProxy(MysqlSession mysqlSession, MyConfiguration myConfiguration) { this.mysqlSession = mysqlSession; this.myConfiguration = myConfiguration; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MapperBean mapperBean = myConfiguration.readMapper("UserMapper.xml"); // 判断是否是xml文件对应的接口 if (!method.getDeclaringClass().getName().equals(mapperBean.getInterfaceName())) { return null; } List<Function> list = mapperBean.getList(); if (!CollectionUtils.isEmpty(list)) { for (Function x : list) { if (judge(method, x, "selectById")) { return mysqlSession.selectOne(x.getSql(), String.valueOf(args[0])); } if (judge(method, x, "deleteById")) { mysqlSession.deleteOne(x.getSql(), String.valueOf(args[0])); } if (judge(method, x, "insert")) { mysqlSession.insertOne(x.getSql(), String.valueOf(args[0])); } if (judge(method, x, "updateById")) { mysqlSession.updateOne(x.getSql(), String.valueOf(args[0])); } } } return null; } /** * 判断Mapper文件中id是否一致 */ private boolean judge(Method method, Function x, String methodId) { return method.getName().equals(x.getFunctionName()) && x.getFunctionName().equals(methodId); } }
MysqlSession类,其中getMapper()动态代理的方式获取对象
public class MysqlSession { private Logger logger= LoggerFactory.getLogger(MysqlSession.class); private MyConfiguration myConfiguration=new MyConfiguration(); private Excutor excutor=new ExcutorImpl(); public <T> T selectOne(String statment,Object parameter){ logger.info("我是query的SqlSession!"); return excutor.query(statment,parameter); } public void insertOne(String statment,Object parmeter){ logger.info("我是save的SqlSession!"); excutor.save(statment,parmeter); } public void updateOne(String statment,Object parmeter){ logger.info("我是modify的SqlSession!"); excutor.modify(statment,parmeter); } public void deleteOne(String statment,Object parmeter){ logger.info("我是remove的SqlSession!"); excutor.remove(statment,parmeter); } public <T> T getMapper(Class<T> clazz){ logger.info("已经通过代理获取对象!"); // 动态代理 return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new MyMapperProxy(this, myConfiguration)); } }
增删改查底层接口:Excutor
public interface Excutor { <T> T query(String statement,Object parameter); void save(String statement,Object parameter); void modify(String statement,Object parameter); void remove(String statement,Object parameter); }
增删改查底层接口实现:ExcutorImpl
连接数据库,Jdbc方式进行怎删改查操作 public class ExcutorImpl implements Excutor { private Logger logger= LoggerFactory.getLogger(ExcutorImpl.class); private MyConfiguration myConfiguration=new MyConfiguration(); Connection connection = myConfiguration.getDataSourceInfo(); @Override public <T> T query(String statement, Object parameter) { try{ PreparedStatement preparedStatement = connection.prepareStatement(statement); preparedStatement.setString(1,parameter.toString()); ResultSet resultSet = preparedStatement.executeQuery(); User user=new User(); while (resultSet.next()){ user.setId(resultSet.getInt("id")); user.setUsername(resultSet.getString("username")); user.setPassword(resultSet.getString("password")); user.setGender(resultSet.getInt("gender")); user.setAge(resultSet.getInt("age")); user.setIdcard(resultSet.getInt("idcard")); user.setPhone(resultSet.getInt("phone")); } logger.info("遍历结果集完成!"); return (T)user; }catch (Exception e){ logger.error("查询结果报错"+e.getMessage()); return null; } } @Override public void save(String statement, Object parameter) { try { connection.setAutoCommit(false); PreparedStatement preparedStatement = connection.prepareStatement(statement); User user = JSON.parseObject(String.valueOf(parameter),User.class); preparedStatement.setInt(1,user.getId()); preparedStatement.setString(2,user.getUsername()); preparedStatement.setString(3,user.getPassword()); preparedStatement.setInt(4,user.getGender()); preparedStatement.setInt(5,user.getAge()); preparedStatement.setInt(6,user.getIdcard()); preparedStatement.setInt(7,user.getPhone()); preparedStatement.execute(); comit(connection); logger.info("保存成功!"); } catch (Exception e ) { logger.error("保存失败"+e.getMessage()); rollBack(connection); } } @Override public void modify(String statement, Object parameter) { try { PreparedStatement preparedStatement = connection.prepareStatement(statement); User user = JSON.parseObject(String.valueOf(parameter),User.class); preparedStatement.setInt(3,user.getId()); preparedStatement.setString(1,user.getUsername()); preparedStatement.setString(2,user.getPassword()); preparedStatement.execute(); comit(connection); logger.info("修改成功!"); } catch (SQLException e) { logger.error("修改失败"+e.getMessage()); rollBack(connection); } } @Override public void remove(String statement, Object parameter) { try { PreparedStatement preparedStatement = connection.prepareStatement(statement); preparedStatement.setString(1,parameter.toString()); preparedStatement.execute(); logger.info("删除成功!"); } catch (SQLException throwables) { throwables.printStackTrace(); } } /** * 事物提交 */ private void comit(Connection connection){ try { connection.commit(); logger.error("事物提交成功!"); }catch (Exception e){ logger.error("事物提交失败!"+e.getMessage()); } } /** * 事物回滚 */ private void rollBack(Connection connection){ try{ connection.rollback(); logger.info("事物回滚成功!"); } catch (Exception e){ logger.info("事物回滚失败!"+e.getMessage()); } } }
User实体类
public class User { private int id; private String username; private String password; private int gender; private int age; private int idcard; private int phone; @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", gender=" + gender + ", age=" + age + ", idcard=" + idcard + ", phone=" + phone + '}'; } }
UserMapper类
public interface UserMapper { User selectById(String id); void insert(Object user); void updateById(Object user); void deleteById(String id); }
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <mapper namespace="com.li.mybatisconfig.mapper.UserMapper"> <select id="selectById" resultType="com.li.mybatisconfig.entity.User"> select * from user where id=? </select> <delete id="deleteById" > delete from user where id=? </delete> <insert id="insert"> insert into user (id,username,password,gender,age,idcard,phone) values(?,?,?,?,?,?,?) </insert> <update id="updateById"> update user set username=?,password=? where id=? </update> </mapper>
增删改查测试类:MybatisTestRunner
public class MybatisTestRunner { MysqlSession mysqlSession=new MysqlSession(); UserMapper mapper=mysqlSession.getMapper(UserMapper.class); @Test public void findById(){ User user = mapper.selectById("1"); System.out.println(user); } @Test public void insert(){ User user=new User(); user.setId(2); user.setUsername("test1"); user.setPassword("test1"); mapper.insert(JSON.toJSON(user)); } @Test public void deleteById(){ mapper.deleteById("1"); } @Test public void update(){ User user=new User(); user.setId(2); user.setUsername("test2"); user.setPassword("test2"); mapper.updateById(JSON.toJSON(user)); } } 查询接口方法运行日志打印如下

很Nice 感觉Mybatis 不是那么难 哈哈 哈哈!

代码详情见https://gitee.com/Marlon_Brando/back.git

最新回复(0)