Mini MyBatis-Plus(下)

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
 

最核心的内容前两篇已经讲完了,这一篇只有代码:

先看demo目录下的三个文件:

DemoApplication.java

package com.example.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}

User.java

package com.example.demo;import com.example.demo.mybatisplus.annotations.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;/*** @author mx*/
@Data
@TableName("t_user")
@AllArgsConstructor
@NoArgsConstructor
public class User {private Long id;private String name;private Integer age;private Date birthday;
}

UserMapper.java

package com.example.demo;import com.example.demo.mybatisplus.AbstractBaseMapper;/*** @author mx*/
public class UserMapper extends AbstractBaseMapper<User> {
}

mybatisplus下AbstractBaseMapper.java

package com.example.demo.mybatisplus;import com.example.demo.mybatisplus.annotations.TableName;
import com.example.demo.mybatisplus.core.JdbcTemplate;
import com.example.demo.mybatisplus.query.QueryWrapper;
import com.example.demo.mybatisplus.query.SqlParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;/*** Mapper基类** @author mx*/
public abstract class AbstractBaseMapper<T> {private static Logger logger = LoggerFactory.getLogger(AbstractBaseMapper.class);private JdbcTemplate<T> jdbcTemplate = new JdbcTemplate<T>();private Class<T> beanClass;private final String TABLE_NAME;private static final String DEFAULT_LOGICAL_TYPE = " and ";public AbstractBaseMapper() {// DO对象的ClassbeanClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];// DO对应的表名 TODO 非空判断及默认处理TABLE_NAME = beanClass.getAnnotation(TableName.class).value();}public T select(QueryWrapper<T> queryWrapper) {List<T> list = this.list(queryWrapper);if (!list.isEmpty()) {return list.get(0);}return null;}public List<T> list(QueryWrapper<T> queryWrapper) {StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM ").append(TABLE_NAME).append(" WHERE ");List<Object> paramList = new ArrayList<>();Map<String, SqlParam> conditionMap = queryWrapper.build();conditionMap.forEach((operator, param) -> {sqlBuilder.append(param.getColumnName()).append(operator).append("?").append(DEFAULT_LOGICAL_TYPE);paramList.add(param.getValue());});// 删除最后一个 andString sql = sqlBuilder.replace(sqlBuilder.length() - DEFAULT_LOGICAL_TYPE.length(), sqlBuilder.length(), ";").toString();try {logger.info("sql: {}", sql);logger.info("params: {}", paramList);return jdbcTemplate.queryForList(sql, paramList, beanClass);} catch (Exception e) {e.printStackTrace();logger.error("query failed", e);}return Collections.emptyList();}public int insert(T bean) {// 得到DO对象的所有字段Field[] declaredFields = beanClass.getDeclaredFields();// 拼接sql语句,表名来自DO的TableName注解valueStringBuilder sqlBuilder = new StringBuilder().append("INSERT INTO ").append(TABLE_NAME).append(" VALUES(");for (int i = 0; i < declaredFields.length; i++) {sqlBuilder.append("?");if (i < declaredFields.length - 1) {sqlBuilder.append(",");}}sqlBuilder.append(")");// 收集sql参数ArrayList<Object> paramList = new ArrayList<>();try {for (Field declaredField : declaredFields) {declaredField.setAccessible(true);Object o = declaredField.get(bean);paramList.add(o);}} catch (IllegalAccessException e) {e.printStackTrace();}int affectedRows = 0;try {logger.info("sql: {}", sqlBuilder.toString());logger.info("params: {}", paramList);affectedRows = jdbcTemplate.update(sqlBuilder.toString(), paramList);logger.info("insert success, affectedRows: {}", affectedRows);return affectedRows;} catch (SQLException e) {e.printStackTrace();logger.error("insert failed", e);}return 0;}public int updateSelective(T bean, QueryWrapper<T> queryWrapper) {// 得到DO对象的所有字段Field[] declaredFields = beanClass.getDeclaredFields();// 拼接sql语句,表名来自DO的TableName注解valueStringBuilder sqlSetBuilder = new StringBuilder().append("UPDATE ").append(TABLE_NAME).append(" SET ");List<Object> paramList = new ArrayList<>();// 先拼接要SET的字段占位符 SET name=?, age=?try {for (int i = 0; i < declaredFields.length; i++) {Field declaredField = declaredFields[i];declaredField.setAccessible(true);Object fieldValue = declaredField.get(bean);if (fieldValue != null) {sqlSetBuilder.append(declaredField.getName()).append(" = ").append("?").append(", ");paramList.add(fieldValue);}}} catch (IllegalAccessException e) {e.printStackTrace();}// 删除最后一个 ,sqlSetBuilder = sqlSetBuilder.delete(sqlSetBuilder.length() - 2, sqlSetBuilder.length());// 再拼接WHERE条件占位符StringBuilder sqlWhereBuilder = new StringBuilder(" WHERE ");Map<String, SqlParam> conditionMap = queryWrapper.build();for (Map.Entry<String, SqlParam> stringSqlParamEntry : conditionMap.entrySet()) {String operator = stringSqlParamEntry.getKey();SqlParam param = stringSqlParamEntry.getValue();sqlWhereBuilder.append(param.getColumnName()).append(operator).append("?").append(DEFAULT_LOGICAL_TYPE);paramList.add(param.getValue());}// 删除最后一个 andsqlWhereBuilder = sqlWhereBuilder.replace(sqlWhereBuilder.length() - DEFAULT_LOGICAL_TYPE.length(), sqlWhereBuilder.length(), ";");String sql = sqlSetBuilder.append(sqlWhereBuilder).toString();int affectedRows = 0;try {logger.info("sql: {}", sqlSetBuilder.toString());logger.info("params: {}", paramList);affectedRows = jdbcTemplate.update(sql, paramList);logger.info("update success, affectedRows: {}", affectedRows);return affectedRows;} catch (SQLException e) {e.printStackTrace();logger.error("update failed", e);}return 0;}}

annotations下的TableName.java

package com.example.demo.mybatisplus.annotations;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author mx*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TableName {String value();}

core下的

JdbcTemplate.java

package com.example.demo.mybatisplus.core;import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;/*** JdbcTemplate,简化jdbc操作** @author mx*/
public class JdbcTemplate<T> {public List<T> queryForList(String sql, List<Object> params, RowMapper<T> rowMapper) throws SQLException {return query(sql, params, rowMapper);}public T queryForObject(String sql, List<Object> params, RowMapper<T> rowMapper) throws SQLException {List<T> result = query(sql, params, rowMapper);return result.isEmpty() ? null : result.get(0);}public List<T> queryForList(String sql, List<Object> params, Class<T> clazz) throws Exception {return query(sql, params, clazz);}public T queryForObject(String sql, List<Object> params, Class<T> clazz) throws Exception {List<T> result = query(sql, params, clazz);return result.isEmpty() ? null : result.get(0);}public int update(String sql, List<Object> params) throws SQLException {// 1.获取ConnectionConnection conn = getConnection();// 2.传入sql模板、sql参数,得到PreparedStatementPreparedStatement ps = getPreparedStatement(sql, params, conn);// 3.执行更新(增删改)int affectedRows = ps.executeUpdate();// 4.释放资源closeConnection(conn, ps, null);return affectedRows;}// ************************* private methods **************************private List<T> query(String sql, List<Object> params, RowMapper<T> rowMapper) throws SQLException {// 外部传入rowMapper(手写规则)return baseQuery(sql, params, rowMapper);}private List<T> query(String sql, List<Object> params, Class<T> clazz) throws Exception {// 自己创建rowMapper(反射)后传入BeanHandler<T> beanHandler = new BeanHandler<>(clazz);return baseQuery(sql, params, beanHandler);}/*** 基础查询方法,必须传入Bean的映射规则** @param sql* @param params* @param rowMapper* @return* @throws SQLException*/private List<T> baseQuery(String sql, List<Object> params, RowMapper<T> rowMapper) throws SQLException {// TODO 参数非空校验// 1.获取ConnectionConnection conn = getConnection();// 2.传入sql模板、sql参数,得到PreparedStatementPreparedStatement ps = getPreparedStatement(sql, params, conn);// 3.执行查询ResultSet rs = ps.executeQuery();// 4.处理结果List<T> result = new ArrayList<>();while (rs.next()) {T obj = rowMapper.mapRow(rs);result.add(obj);}// 5.释放资源closeConnection(conn, ps, rs);return result;}/*** 内部类,实现了RowMapper接口,底层使用反射** @param <R>*/private static class BeanHandler<R> implements RowMapper<R> {// clazz表示最终封装的bean类型private Class<R> clazz;public BeanHandler(Class<R> clazz) {this.clazz = clazz;}@Overridepublic R mapRow(ResultSet rs) {try {if (rs.next()) {// 1.获取表数据ResultSetMetaData metaData = rs.getMetaData();// 2.反射创建beanR bean = clazz.newInstance();// 3.利用反射,把表数据设置到bean中for (int i = 0; i < metaData.getColumnCount(); i++) {String name = metaData.getColumnName(i + 1);Object value = rs.getObject(name);Field field = clazz.getDeclaredField(name);field.setAccessible(true);field.set(bean, value);}// 4.返回beanreturn bean;} else {return null;}} catch (Exception e) {throw new RuntimeException(e);}}}private PreparedStatement getPreparedStatement(String sql, List<Object> params, Connection conn) throws SQLException {// 1.传入sql模板,得到PreparedStatementPreparedStatement ps = conn.prepareStatement(sql);// 2.为sql模板设置参数for (int i = 0; i < params.size(); i++) {ps.setObject(i + 1, params.get(i));}return ps;}private Connection getConnection() throws SQLException {// TODO 可以抽取配置到properties文件String url = "jdbc:mysql://localhost:3306/demo";String user = "root";String password = "123456";return DriverManager.getConnection(url, user, password);}private void closeConnection(Connection conn, PreparedStatement preparedStatement, ResultSet rs) throws SQLException {if (rs != null) {rs.close();}if (preparedStatement != null) {preparedStatement.close();}if (conn != null) {conn.close();}}}

 RowMapper.java

package com.example.demo.mybatisplus.core;import java.sql.ResultSet;/*** 结果集映射器** @author mx*/
@FunctionalInterface
public interface RowMapper<T> {/*** 将结果集转为指定的Bean** @param resultSet* @return*/T mapRow(ResultSet resultSet);
}

query下的

QueryWrapper.java

package com.example.demo.mybatisplus.query;import com.example.demo.mybatisplus.utils.ConditionFunction;
import com.example.demo.mybatisplus.utils.Reflections;import java.util.HashMap;
import java.util.Map;/*** 模拟MyBatis-Plus的LambdaQueryWrapper(思路完全不同,仅仅是形似)** @author mx*/
public class QueryWrapper<T> {// conditionMap,收集查询条件// {//    " LIKE ": {//        "name": "bravo1988"//    },//    " = ": {//        "age": 18//    }// }private final Map<String, SqlParam> conditionMap = new HashMap<>();// 操作符类型,比如 name like 'bravo' 中的 LIKEprivate static final String OPERATOR_EQ = " = ";private static final String OPERATOR_GT = " > ";private static final String OPERATOR_LT = " < ";private static final String OPERATOR_LIKE = " LIKE ";public QueryWrapper<T> eq(ConditionFunction<T, ?> fn, Object value) {String columnName = Reflections.fnToColumnName(fn);conditionMap.put(OPERATOR_EQ, new SqlParam(columnName, value));return this;}public QueryWrapper<T> gt(ConditionFunction<T, ?> fn, Object value) {String columnName = Reflections.fnToColumnName(fn);conditionMap.put(OPERATOR_GT, new SqlParam(columnName, value));return this;}public QueryWrapper<T> lt(ConditionFunction<T, ?> fn, Object value) {String columnName = Reflections.fnToColumnName(fn);conditionMap.put(OPERATOR_LT, new SqlParam(columnName, value));return this;}public QueryWrapper<T> like(ConditionFunction<T, ?> fn, Object value) {String columnName = Reflections.fnToColumnName(fn);conditionMap.put(OPERATOR_LIKE, new SqlParam(columnName, "%" + value + "%"));return this;}public Map<String, SqlParam> build() {return conditionMap;}
}

 SqlParam.java

package com.example.demo.mybatisplus.query;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @author mx*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SqlParam {private String columnName;private Object value;
}

utils下的

 ConditionFunction.java

package com.example.demo.mybatisplus.utils;import java.io.Serializable;
import java.util.function.Function;/*** 扩展java.util.function包下的Function接口:支持Serializable* 搭配Reflections工具类一起使用,用于获取Lambda表达式的方法名** @author mx*/
@FunctionalInterface
public interface ConditionFunction<T, R> extends Function<T, R>, Serializable {
}

Reflections.java

package com.example.demo.mybatisplus.utils;import java.beans.Introspector;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.regex.Pattern;/*** 获取Lambda入参的方法名** @author mx*/
public class Reflections {private static final Pattern GET_PATTERN = Pattern.compile("^get[A-Z].*");private static final Pattern IS_PATTERN = Pattern.compile("^is[A-Z].*");/*** 注意: 非标准变量(非小驼峰)调用这个方法可能会有问题** @param fn* @param <T>* @return*/public static <T> String fnToColumnName(ConditionFunction<T, ?> fn) {try {Method method = fn.getClass().getDeclaredMethod("writeReplace");method.setAccessible(Boolean.TRUE);SerializedLambda serializedLambda = (SerializedLambda) method.invoke(fn);String getter = serializedLambda.getImplMethodName();// 对于非标准变量生成的Get方法这里可以直接抛出异常,或者打印异常日志if (GET_PATTERN.matcher(getter).matches()) {getter = getter.substring(3);} else if (IS_PATTERN.matcher(getter).matches()) {getter = getter.substring(2);}return Introspector.decapitalize(getter);} catch (ReflectiveOperationException e) {throw new RuntimeException(e);}}
}

其实第一篇的内容是最难的,不只是从0到1,而是从0到90,后面两篇其实只是90到100,在这基础稍微扩展了一下而已。

AbstractBaseMapper代码还有冗余,有兴趣的同学可以自行完善。但还是那句话,如果你的目的是为了锻炼封装能力,可以精益求精,但我们的AbstractBaseMapper注定不能用于生产,即使要优化,点到为止即可。

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析

阶段4、深入jdk其余源码解析

阶段5、深入jvm源码解析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2661447.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

V8提升对象属性访问速度---快属性和慢属性

V8提升对象属性访问速度—快属性和慢属性 在 JavaScript 中&#xff0c;对象就像一个字典&#xff0c;可以使用字符串作为键名&#xff0c;任意对象作为键值。早期的实现方式是使用字典来存储对象的属性。 字典是非线性的数据结构&#xff0c;查询效率会低于线性的数据结构&a…

ssm基于JAVA的学生在线考试系统+vue论文

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统考试信息管理难度大&#xff0c;容错率低&#xff0c;管理…

flutter学习-day23-使用extended_image处理图片的加载和操作

文章目录 1. 介绍2. 属性介绍3. 使用 1. 介绍 在 Flutter 的开发过程中&#xff0c;经常会遇到图片的显示和加载处理&#xff0c;通常显示一个图片&#xff0c;都有很多细节需要处理&#xff0c;比如图片的加载、缓存、错误处理、图片的压缩、图片的格式转换等&#xff0c;如果…

数字化转型:从数据资产到旅程指标

很多面临数字化转型的企业都在关注数据资产和旅程指标。在数字化转型之后&#xff0c;一个常见问题就是数据暴涨。从传统渠道扩展到 APP、小程序等线上化渠道之后&#xff0c;公司会面临 5 倍、10 倍甚至几十倍的数据增长。除了带来技术架构的挑战&#xff0c;还会带来数据资产…

设计模式(4)--对象行为(7)--观察者

1. 意图 定义对象间的一种一对多的依赖关系&#xff0c; 当一个对象的状态改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。 2. 四种角色 抽象目标(Subject)、具体目标(Concrete Subject)、抽象观察者(Observer)、 具体观察者(Concrete Observer) 3. 优点 3.1 …

《Spring Cloud学习笔记:Nacos配置管理 OpenFeign LoadBalancer Getway》

基于Feign的声明式远程调用&#xff08;代码更优雅&#xff09;&#xff0c;用它来去代替我们之前的RestTemplate方式的远程调用 1. Nacos配置管理&#xff1a;Nacos Config 服务配置中心介绍 首先我们来看一下,微服务架构下关于配置文件的一些问题&#xff1a; 配置文件相…

Netty—Reactor线程模型详解

文章目录 前言线程模型基本介绍线程模型分类Reactor线程模型介绍Netty线程模型&#xff1a; 传统阻塞IO的缺点Reactor线程模型单Reactor单线程模式单Reactor多线程模式主从Reactor多线程Reactor 模式小结 Netty 线程模型案例说明&#xff1a;Netty核心组件简介ChannelPipeline与…

记一次接口交互is开头的属性序列化后“is”丢失问题

问题背景&#xff1a; 今天在做项目联调时调用别人的第三方接口时&#xff0c;发现字段传递不对导致参数传递异常的问题&#xff0c;当时还很奇怪&#xff0c;明白传好着呢&#xff0c;怎么就好端端的出现字段不对的情况呢&#xff1f; 查看发现该字段为boolean类型的isIsRef…

node fs模块读取文件 readFile、readFileSync、fsPromises.readFile、createReadStream

文章目录 1.读取文件1.1 readFile1.2 readFileSync1.3 fsPromises.readFile&#xff1a;promise的写法1.4 fs.createReadStream 1.读取文件 readFile&#xff1a;异步读取文件readFileSync&#xff1a;同步读取文件fsPromises.readFile&#xff1a;promise的写法 需要注意的是…

vue data变量不能以“_”开头,否则会产生很多怪异问题

1、 比如给子组件赋值&#xff0c;子组件无法得到这个值&#xff08;也不是一直无法得到&#xff0c;设置后this.$forceUpdate() 居然可以得到&#xff09;&#xff0c; 更无法watch到 <zizujian :config"_config1"> </zizujian>this._config1 { ...…

【微服务核心】MyBatis Plus

MyBatis Plus 文章目录 MyBatis Plus1. 简介2. 入门使用3. 核心功能3.1 CRUD 接口3.1.1 Mapper CRUD 接口3.1.2 Service CRUD 接口 3.2 条件构造器3.3 分页插件3.4 Mybatis-Plus 注解 4. 拓展4.1 逻辑删除4.2 MybatisX快速开发插件 5. 插件5.1 [分页插件](#page)5.2 乐观锁插件…

如何从RTP包的AP类型包,获取h265的PPS、SPS、VPS信息

ffmpeg播放rtp流&#xff0c;为了降低首开延迟&#xff0c;需要在SDP文件中指定PPS、SPS、VPS信息。抓包后发现wireshark无法解析AP包。需要自己进行AP包解析。RTP协议AP包格式如下&#xff1a; 根据如上信息&#xff0c;我们可以解析AP包&#xff0c;效果如下 40 01&#xff…

Windows上安装NodeJs

Windows上安装NodeJs 一、操作环境 操作系统: Windows 10 专业版 SDK:NodeJs v16.19.1&#xff08;安装鸿蒙IDE自动安装的NodeJs&#xff09; 二、安装过程 2.1下载Node.js安装包 官网下载地址&#xff1a; 下载历史版本安装也可 2.2 双击下载好的安装文件 2.3 打开下载…

ClickHouse基础知识(五):ClickHouse的SQL 操作

基本上来说传统关系型数据库&#xff08;以 MySQL 为例&#xff09;的 SQL 语句&#xff0c;ClickHouse 基本都支持&#xff0c; 这里不会从头讲解 SQL 语法只介绍 ClickHouse 与标准 SQL&#xff08;MySQL&#xff09;不一致的地方。 1. Insert 基本与标准 SQL&#xff08;My…

如何基于一台MacBook搞定企业级大模型知识库部署

▼最近直播超级多&#xff0c;预约保你有收获 今晚直播&#xff1a;《ChatGLM3/Qwen业务案例实战》 —1— 为什么要在 MacBook 上搭建知识库&#xff1f; 最核心最重要的是我们手上的文档资料出于安全要求&#xff0c;不能随便上传到云服务&#xff0c;也就无法实际验证知识库的…

统信操作系统中QWebEngine(js与C++交互)

一、直接方式C++调用JS函数 1.1 编写C++界面框架 界面有两个控件组成。QWebEngineView和QPushButton组成。上面部分位webengineview 下面部分位C++的按钮。 处理逻辑是希望按钮按下QPushButton的直接调用QWebEngineView中js定义好的函数。 大致内容如下图: mainwindow.cpp文…

刷算法-- leetcode 96. 不同的二叉搜索树

思路 观察树的组成&#xff0c;可以发现n3时的二叉搜索树可以由&#xff0c;头节点分别为1、2、3时的所有结果组成&#xff01;定义dp[i]为由i个节点组成的二叉搜索树的个数。确定递推公式&#xff0c;dp[i] 由1为头节点组成的二叉搜索树个数由2为头组成的个数…由i为头节点组…

Go 泛型之泛型约束

Go 泛型之泛型约束 文章目录 Go 泛型之泛型约束一、引入二、最宽松的约束&#xff1a;any三、支持比较操作的内置约束&#xff1a;comparable四、自定义约束五、类型集合&#xff08;type set&#xff09;六、简化版的约束形式七、约束的类型推断八、小结 一、引入 虽然泛型是…

等保测评里面,帐号和短信验证码双因子认证机制

短信验证参数检查过滤器 public class MultiTextMessageFilter implements Filter {private AntPathRequestMatcher matcher new AntPathRequestMatcher("/oauth/token");Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterC…

电脑亮度怎么调?揭开亮度调节的秘密!

电脑亮度调节是一项基本的系统设置&#xff0c;它不仅影响我们的视觉体验&#xff0c;还有助于减轻眼部疲劳。可是电脑亮度怎么调呢&#xff1f;本文将介绍三种不同的电脑亮度调节方法&#xff0c;适用于Windows和常见的笔记本电脑&#xff0c;让您的屏幕明亮如新。 方法1&…