手写MyBatis
1. 前言
本篇博客,将使用JDK动态代理、注解、反射等技术,编写一个最简单的MyBatis,可基本实现对象的增删查改
2. 注解的定义
2.1 Delete注解
/**
* @ClassName Delete
* @Descriiption 删除注解
* @Author yanjiantao
* @Date 2019/6/27 11:03
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Delete {
public String value();
}
2.2 Insert注解
/**
* @ClassName Delete
* @Descriiption 保存注解
* @Author yanjiantao
* @Date 2019/6/27 11:03
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Insert {
public String value();
}
2.3 Select注解
/**
* @ClassName Delete
* @Descriiption 查询注解
* @Author yanjiantao
* @Date 2019/6/27 11:03
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {
public String value();
}
2.4 Update注解
/**
* @ClassName Delete
* @Descriiption 更新注解
* @Author yanjiantao
* @Date 2019/6/27 11:03
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Update {
public String value();
}
3. jdk动态代理
3.1 方法代理类
/**
* @ClassName MethodProxy
* @Descriiption 方法代理
* @Author yanjiantao
* @Date 2019/6/27 11:11
**/
public class MethodProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return DaoOperatorHandler.handle(method,args);
}
}
该类实现JDK的InvocationHandler方法,并且实验invoke方法,即可实现JDK的动态代理
3.2 动态代理工厂类
/**
* @ClassName MethodProxyFactory
* @Descriiption 代理工厂类
* @Author yanjiantao
* @Date 2019/6/28 15:40
**/
public class MethodProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T getBean(Class<T> clazz) {
final MethodProxy methodProxy = new MethodProxy();
return (T) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{clazz},
methodProxy
);
}
}
该工厂的方法主要是得到Mapper的实例,并且把Mapper交给JDK进行动态代理
4. 数据库操作
4.1 数据库操作处理类
/**
* @ClassName DaoOperatorHandler
* @Descriiption 数据库操作处理器
* @Author yanjiantao
* @Date 2019/6/27 11:39
**/
public class DaoOperatorHandler {
public static Object handle(Method method, Object[] parameters) throws SQLException, ClassNotFoundException {
String sql = null;
// 插入
if (method.isAnnotationPresent(Insert.class)) {
sql = checkSql(method.getAnnotation(Insert.class).value(), Insert.class.getSimpleName());
insert(sql, parameters);
// 更新
}else if (method.isAnnotationPresent(Update.class)) {
sql = checkSql(method.getAnnotation(Update.class).value(), Update.class.getSimpleName());
return update(sql, parameters);
// 查询
}else if (method.isAnnotationPresent(Select.class)) {
sql = checkSql(method.getAnnotation(Select.class).value(), Select.class.getSimpleName());
Class returnType = method.getReturnType();
if (List.class.isAssignableFrom(returnType)) {
return selectMany(sql, parameters);
}else {
return selectMany(sql, parameters).get(0);
}
}else if (method.isAnnotationPresent(Delete.class)) {
sql = checkSql(method.getAnnotation(Delete.class).value(), Delete.class.getSimpleName());
return update(sql, parameters);
}
System.out.println(sql);
return null;
}
/**
* 插入
* @param sql sql
* @param parameters 参数
* @throws SQLException SQLException
* @throws ClassNotFoundException ClassNotFoundException
*/
private static void insert(String sql, Object[] parameters) throws SQLException, ClassNotFoundException {
Connection connection = JDBCUtils.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
for (int i = 0; i < parameters.length; i++) {
statement.setObject(i+1, (String) parameters[i]);
}
statement.execute();
connection.close();
}
/**
* 插入
* @param sql sql
* @param parameters 参数
* @throws SQLException SQLException
* @throws ClassNotFoundException ClassNotFoundException
*/
private static Integer update(String sql, Object[] parameters) throws SQLException, ClassNotFoundException {
Connection connection = JDBCUtils.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
for (int i = 0; i < parameters.length; i++) {
statement.setObject(i+1, parameters[i]);
}
int result = statement.executeUpdate();
connection.close();
return result;
}
/**
* 插入
* @param sql sql
* @param parameters 参数
* @return List<T>
* @throws SQLException SQLException
* @throws ClassNotFoundException ClassNotFoundException
*/
private static <T> List<T> selectMany(String sql, Object[] parameters) throws SQLException, ClassNotFoundException {
Connection connection = JDBCUtils.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
for (int i = 0; parameters != null && i < parameters.length; i++) {
statement.setObject(i+1, parameters[i]);
}
ResultSet resultSet = statement.executeQuery();
List<T> result = new ResultToMapper<T>().mapToObject(resultSet,User.class);
return result;
}
/**
* 检查sql
* @param sql sql
* @param type type
* @return the sql
* @throws SQLException SQLException
*/
private static String checkSql(String sql, String type) throws SQLException {
String sqlType = sql.split(" ")[0];
if (!sqlType.equalsIgnoreCase(type)) {
throw new SQLException("SQL语句错误");
}
return sql;
}
}
该类主要是根据被代理类是否包含相关注解,根据注解的类型,进行增删查改的操作,最后,再将增删查改后的处理结果,使用反射映射到实体类上
5 实体类
5.1用户实体类
/**
* @ClassName User
* @Descriiption 用户实体类
* @Author yanjiantao
* @Date 2019/6/28 15:24
**/
@Data
public class User {
private Integer id;
private String username;
private String password;
}
6 工具类
6.1 JDBCUtils
/**
* @ClassName JDBCUtils
* @Descriiption jdbc连接工具类
* @Author yanjiantao
* @Date 2019/6/28 16:24
**/
public class JDBCUtils {
public static Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
String username = "root";
String password = "root123456";
return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&serverTimezone=GMT%2B8", username, password);
}
}
6.2 ResultToMapper
/**
* @ClassName ResultToMapper
* @Descriiption mysql查询结果转换为实体bean
* @Author yanjiantao
* @Date 2019/6/28 17:35
**/
public class ResultToMapper<T> {
public List<T> mapToObject(ResultSet resultSet, Class<?> clazz) {
if (resultSet == null) {
return null;
}
List<T> result = null;
try {
while (resultSet.next()) {
T bean = (T) clazz.newInstance();
ResultSetMetaData metaData = resultSet.getMetaData();
for (int i = 0; i < metaData.getColumnCount(); i++) {
String columnName = metaData.getColumnName(i + 1);
Object columnValue = resultSet.getObject(i + 1);
Field field = clazz.getDeclaredField(columnName);
if (field != null && columnValue != null) {
field.setAccessible(true);
field.set(bean,columnValue);
}
}
if (result == null) {
result = new ArrayList<>();
}
result.add(bean);
}
} catch (SQLException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
if (result == null) {
return Collections.emptyList();
}
return result;
}
}
该类主要是将mysql查询的结果,通过反射,映射到实体类上
7 Mapper
public interface UserMapper {
@Insert("insert into user (username,password) values (?,?)")
public void addUser(String name, String password);
@Select("select * from user")
public List<User> findUsers();
@Select("select * from user where id = ?")
public User getUser(Integer id);
@Update("update user set username = ? , password=? where id=?")
public Integer updateUser(String name, String password, Integer id);
@Delete("delete from user where id=?")
public Integer deleteUser(Integer id);
}
8 测试类
@Slf4j
public class UserMapperTest {
@Test
public void addUser() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
userMapper.addUser("boolean-","123456");
log.info("---------->");
}
@Test
public void findUsers() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
List<User> list = userMapper.findUsers();
log.info("---------->list={}", list);
}
@Test
public void getUser() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
User user = userMapper.getUser(2);
log.info("---------->user={}", user);
}
@Test
public void updateUser() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
Integer result = userMapper.updateUser("鄢剑涛update", "yjt123", 1);
log.info("count={}", result);
}
@Test
public void deleteUser() {
UserMapper userMapper = MethodProxyFactory.getBean(UserMapper.class);
Integer count = userMapper.deleteUser(1);
log.info("count={}", count);
}
}
9 总结
这次的编写简单的mybatis,让我对java基础有了进一步的了解,明白了反射、注解的厉害之处,也了解了JDK动态代理设计模式,总之,收获很大!!
手写MyBatis的更多相关文章
- 要想精通Mybatis?从手写Mybatis框架开始吧!
1.Mybatis组成 动态SQL Config配置 Mapper配置 2.核心源码分析 Configuration源码解析 SqlSessionFactory源码解析 SqlSession源码解析 ...
- 手写MyBatis ORM框架实践
一.实现手写Mybatis三个难点 1.接口既然不能被实例化?那么我们是怎么实现能够调用的? 2.参数如何和sql绑定 3.返回结果 下面是Mybatis接口 二.Demo实现 1.创建Maven工程 ...
- 手写mybatis框架笔记
MyBatis 手写MyBatis流程 架构流程图 封装数据 封装到Configuration中 1.封装全局配置文件,包含数据库连接信息和mappers信息 2.封装*mapper.xml映射文件 ...
- 手写MyBatis流程
MyBatis 手写MyBatis流程 架构流程图 封装数据 封装到Configuration中 1.封装全局配置文件,包含数据库连接信息和mappers信息 2.封装*mapper.xml映射文件 ...
- 手写mybatis框架-增加缓存&事务功能
前言 在学习mybatis源码之余,自己完成了一个简单的ORM框架.已完成基本SQL的执行和对象关系映射.本周在此基础上,又加入了缓存和事务功能.所有代码都没有copy,如果也对此感兴趣,请赏个Sta ...
- 带码农《手写Mybatis》进度3:实现映射器的注册和使用
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获!
- 框架源码系列十二:Mybatis源码之手写Mybatis
一.需求分析 1.Mybatis是什么? 一个半自动化的orm框架(Object Relation Mapping). 2.Mybatis完成什么工作? 在面向对象编程中,我们操作的都是对象,Myba ...
- 从零搭建Spring Boot脚手架(4):手写Mybatis通用Mapper
1. 前言 今天继续搭建我们的kono Spring Boot脚手架,上一文把国内最流行的ORM框架Mybatis也集成了进去.但是很多时候我们希望有一些开箱即用的通用Mapper来简化我们的开发.我 ...
- 手写mybatis框架
前言 很久没有更新mybatis的源码解析了,因为最近在将自己所理解的mybatis思想转为实践. 在学习mybatis的源码过程中,根据mybatis的思想自己构建了一个ORM框架 .整个代码都是自 ...
- Mybatis执行流程学习之手写mybatis雏形
Mybatis是目前开发中最常用的一款基于ORM思想的半自动持久层框架,平时我们都仅仅停留在使用阶段,对mybatis是怎样运行的并不清楚,今天抽空找到一些资料自学了一波,自己写了一个mybatis的 ...
随机推荐
- Seata源码—4.全局事务拦截与开启事务处理
大纲 1.Seata Server的启动入口的源码 2.Seata Server的网络服务器启动的源码 3.全局事务拦截器的核心变量 4.全局事务拦截器的初始化源码 5.全局事务拦截器的AOP切面拦截 ...
- 操作系统:苹果的M1芯片因何而快?
本届来看看操作系统下面的硬件层面,重点研究一下 CPU 的原理和它的加速套路. CPU的原理初探 从操作系统的位置来看,它除了能够向上封装,为软件调用提供 API(也就是系统调用),向下又对硬件资源进 ...
- 网络编程:使用poll单线程处理所有I/O事件
事件驱动模型 事件驱动的好处:占用资源少,效率高,可扩展性强,是支持高性能高并发的不二之选. 事件驱动模型也叫作反应堆模型(reactor),或者是Event loop模型,该模型的核心有两点: 1. ...
- codeup之沙漏图形
Description 问题:输入n,输出正倒n层星号三角形.首行顶格,星号间有一空格,效果见样例 输入样例: 3 输出样例: * * * * * * * * * * * 数据规模 1<= n ...
- Partition to K Equal Sum Subsets——LeetCode进阶路
原题链接https://leetcode.com/problems/partition-to-k-equal-sum-subsets/ 题目描述 Given an array of integers ...
- @FeignClient注解配置局部超时时间、OkHttp长连接和SocketTimeoutException异常解决办法
问题描述:open feign配置OKhttp调用远程API,连续调用次数较少时,一切正常,次数非常多时(例如,连续请求600次)就抛出java.net.SocketTimeoutException: ...
- java返回数据库中层级结构数据的treeListMap写法,以动态菜单为例
说明 1.treeListMap写法,与纯算法版本比起来,treeListMap版的缺点是会查询多次数据库,而纯算法只会查询一次数据库 2.里面有不同角色分配不同的菜单相关代码,注意复制粘贴. 3.可 ...
- 「Note」您想来点数据结构吗?
大分块系列 最初分块 \(\color{black}{P4119}\) 考虑数列分块+值域分块 数列分块需要维护: \(nid_{i,j}\) \(fid_i\) \(f_i\) 块 \(i\) 中数 ...
- python基础—数字,字符串练习题
1.如有以下变量 n1=5,请使用 int 的提供的方法,得到该变量最少可以用多少个二进制位表示? n1=5 r=n1.bit_lenght() #当前数字的二进制,至少用n位表示.bit_lengh ...
- java把mysql的数据同步到prometheus
1.mysql的数据 2.java代码 建立指标Collector类,指标类必须继承Collector import cn.hutool.extra.spring.SpringUtil; impo ...