聊聊MyBatis的历史

编程入门 行业动态 更新时间:2024-10-12 14:27:12

聊聊MyBatis的<a href=https://www.elefans.com/category/jswz/34/1770089.html style=历史"/>

聊聊MyBatis的历史

目录

序言

原生的mybatis(使用xml)

关于别名

关于namespace

原生的mybatis(使用注解)

Mybatis与spring

MapperFactoryBean

SqlSessionDaoSupport 

Mybatis与springBoot

Mybatis与mybatis-plus


序言

最近项目中,需要在mysql里面新增一个表,然后套用mybatis做增删改查,然后再mapper.java的方法上写注解。写着写着就感觉很烦,为什么呢?看下面:  

@Insert({"INSERT INTO " + TABLE_NAME + INSERT_COLUMNS +" VALUES" +"(#{id},now(),now(),#{idxConfigId},#{userId},#{ownerId},#{status}," +" #{ukIdempotent},#{startTime},#{endTime},#{extraInfo}," +"  #{awardResult},#{boxConfig},#{currentCount},#{treeId},#{target},#{scope}   )"})
int insertFinanceInfo(FarmFinanceInfoDO financeInfoDO);@Update({ "update "+TABLE_NAME+" set " +" idx_config_id = #{idxConfigId}, " +" owner_id = #{ownerId},status = #{status}, " +" uk_idempotent = #{ukIdempotent}, " +" start_time = #{startTime,jdbcType=TIMESTAMP}, " +" end_time = #{endTime,jdbcType=TIMESTAMP}, " +" current_count = #{currentCount}, " +" tree_id = #{treeId},target = #{target}, scope = #{scope}, " +" box_config = #{boxConfig},award_result = #{awardResult},  " +" extra_info = #{extraInfo} " +"  where id = #{id} and user_id= #{userId}" })
int updateFinanceInfo(FarmFinanceInfoDO financeInfoDO);

其实我是特别不喜欢在java文件里写这种很长的注解的,感觉特别乱,我甚至愿意把它放到xml里面。后来在网上搜了一下原来还有一个叫mybatis-plus的组件,看着还不错,然后在应用过程中也发生了一下问题,问题的根源就是自己还是急于求成,直接在网上搜一下现成的不成体系的代码就用,后面自然一堆问题。然后这两天就稍微挤出一点时间,把mybatis的使用,从最开始的原生xml,到注解,到spring,到spring-boot到mybatis-plus的使用都梳理了一遍。

原生的mybatis(使用xml)

POM如下:

     <!--  --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.18</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.7</version></dependency>

下面mybatis的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis//DTD Config 3.0//EN" ".dtd">
<configuration><!-- 别名 --><typeAliases><package name="entity"/></typeAliases><!-- 数据库环境 --><environments default="development"><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/test_db?characterEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><!-- 映射文件 --><mappers><mapper resource="mapper/user.xml"/></mappers></configuration>

下面是某个mapper的配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis//DTD Mapper 3.0//EN"".dtd"><mapper namespace="xml"><select id="listUser" resultType="user">select * from  user;</select>
</mapper>

这个mapper里面有个namespace,我一直不知道它是什么意思。没关系这个namespace先随便写,一会就知道它是什么意思了。

增加entity

package entity;/*** @program: parent_pro* @description:* @author: 渭水* @create: 2021/07/21*/
public class User {private int id;private String username;private String  password;private String nickname;// 省略getset
}

最后的启动类

package xml;import entity.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;
import java.util.List;/*** @program: parent_pro* @description:* @author: 渭水* @create: 2021/07/21*/
public class MyBatisWithXML {public static void main(String[] args) {String resource = "mybatis-config.xml";InputStream inputStream = null;try {inputStream = Resources.getResourceAsStream(resource);} catch (IOException e) {e.printStackTrace();}SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession session = sqlSessionFactory.openSession();// 最后通过 session 的 selectList() 方法调用 sql 语句 listStudentList<User> userList = session.selectList("listUser");for (User student : userList) {System.out.println("ID:" + student.getId() + ",NAME:" + student.getUsername());}}
}

我们看看整个项目个结构图:

数据库那边,我已经建立好了,大家看看:

运行项目就能看到:

OK,至此项目已经可以运行了。
然后我们分析一下代码:

关于别名

一段一段看

<select id="listUser" resultType="user">select * from  user limit 5;
</select>

这个里面的resultType,我们不用看框架,大概就懂它指的是把下面的sql语句执行的结果,包装成什么类型。

那么光写个user,mybatis知道这个user是个什么东西么?

<!-- 别名 -->
<typeAliases><package name="entity"/>
</typeAliases>

上面一段代码的语义就是,把entity下面的实体都加一个别名,而别名就是类名。所以mybatis就知道上面的user其实对应的就是entity.User这个类。

关于namespace

上面已经有了一个mapper,我现在再加一个mapper如下:

 和之前那个的区别就是一个limit 5,一个limit 10

然后让mybatis的主配置文件感知到这个user2.xml

<!-- 映射文件 -->
<mappers><mapper  resource="mapper/user.xml"/><mapper  resource="mapper/user2.xml"/>
</mappers>

然后运行代码会发现:

 说的很清楚listUser这个id重复了。

在线上工程里,要保证所有的sql的id都不重复有点困难,所以引入了namespace的概念。如果一旦有重复的id,那么session调用的时候得指定namespace。

List<User> userList = session.selectList("xml.listUser");

关于resultMap

上面的例子里咱们的java类都是简单的字符串和数字,如果是一个比较复杂的结构呢?例如一个Family里面有个list,每个元素都是一个Person。怎么办?DB里面对于的表里面list使用json存储的。怎么把json转化成咱们的Person呢?

这里就要提到一个resultMap这个东西了

我们先看怎么使用

<resultMap id="res_family" type="com.alibaba.entity.Family"><result column="family_name" property="familyName"/><result column="car" property="car"typeHandler="com.alibaba.entity.handler.JsonTypeHandler"javaType="com.alibaba.entity.Car"/><result column="family_persion_list" property="familyPersionList"typeHandler="com.alibaba.entity.handler.ListTypeHandler"javaType="com.alibaba.entity.Lover"/></resultMap><select  resultMap="res_family" id="getAll">select * from family</select><insert id="insert">insert into family (family_name,car,family_persion_list)values( #{familyName},#{car ,javaType=com.alibaba.entity.Car, typeHandler = com.alibaba.entity.handler.JsonTypeHandler},#{familyPersionList ,javaType=com.alibaba.entity.Person, typeHandler = com.alibaba.entity.handler.ListTypeHandler})</insert>

通过上面的代码,我们就是猜都能猜处理,使用getAll查询的时候,结果使用res_family来组织,最终装配成com.alibaba.entity.Family。mysql表里面的family_persion_list自动被转化成Lover。这里面涉及到两个Handler。

package com.alibaba.entity.handler;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;import java.io.IOException;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class JsonTypeHandler<T> extends BaseTypeHandler<T> {private static ObjectMapper objectMapper;private Class<T> type;static {objectMapper = new ObjectMapper();}public JsonTypeHandler(Class<T> type) {if (type == null) {throw new IllegalArgumentException("Type argument cannot be null");}this.type = type;}private  T parse(String json) {try {if(json == null || json.length() == 0) {return null;}return objectMapper.readValue(json, type);} catch (IOException e) {throw new RuntimeException(e);}}private String toJsonString(Object obj) {try {return objectMapper.writeValueAsString(obj);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}@Overridepublic T getNullableResult(ResultSet rs, String columnName) throws SQLException {return (T) parse(rs.getString(columnName));}@Overridepublic T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return (T) parse(rs.getString(columnIndex));}@Overridepublic T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return (T) parse(cs.getString(columnIndex));}@Overridepublic void setNonNullParameter(PreparedStatement ps, int columnIndex, T parameter, JdbcType jdbcType) throws SQLException {ps.setString(columnIndex, toJsonString(parameter));}}package com.alibaba.entity.handler;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.apachemons.lang3.StringUtils;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.TypeHandler;
import org.springframework.util.CollectionUtils;import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;/*** 处理jsonArray字符串为pojoList*** @author zj* @date 2020/1/8*/
@MappedJdbcTypes({JdbcType.VARCHAR})
public class ListTypeHandler<T extends Object> implements TypeHandler<List<T>> {private List<T> getListByJsonArrayString(String content) {if (StringUtils.isEmpty(content)) {return new ArrayList<>();}return JSON.parseObject(content, new TypeReference<ArrayList<T>>() {});}/*** 用于定义在Mybatis设置参数时该如何把Java类型的参数转换为对应的数据库类型** <PRE>* PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES* SET SALARY = ? WHERE ID = ?");* pstmt.setBigDecimal(1, 153833.00)* pstmt.setInt(2, 110592)* </PRE>** @param preparedStatement An object that represents a precompiled SQL statement* @param i                 当前参数的位置* @param t           当前参数的Java对象* @param jdbcType          当前参数的数据库类型* @throws SQLException*/@Overridepublic void setParameter(PreparedStatement preparedStatement, int i, List<T> t, JdbcType jdbcType) throws SQLException {if (CollectionUtils.isEmpty(t)) {preparedStatement.setString(i, null);} else {preparedStatement.setString(i, JSON.toJSONString(t));}}@Overridepublic List<T> getResult(ResultSet resultSet, String s) throws SQLException {return getListByJsonArrayString(resultSet.getString(s));}@Overridepublic List<T> getResult(ResultSet resultSet, int i) throws SQLException {return getListByJsonArrayString(resultSet.getString(i));}@Overridepublic List<T> getResult(CallableStatement callableStatement, int i) throws SQLException {return getListByJsonArrayString(callableStatement.getString(i));}
}

大概能看懂 就是一个把json转成pojo,一个把json转成List<Pojo>。

对应的sql语句如下:

CREATE TABLE `family` (`family_name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,`car` json DEFAULT NULL,`family_persion_list` json DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

对应的实体类

package com.alibaba.entity;import lombok.Data;import java.util.List;@Data
public class Family {private String familyName;private Car car;private  List<Lover> familyPersionList;
}

上面的Car,Lover就是两个最简单的POJO 里面的字段大家自己写吧。

原生的mybatis(使用注解)

@Mapper
public interface PersonMapper {@Select("select id,name ,age from person where id = #{id}")Person getPersonById(int id);@Select("<script>"+"select * from person where 1=1"+"<if test='name2 != null'> and name = #{name2}"   +"</if>"+"<if test='age != null'> and age = #{age}"   +"</if>"+"</script>")List<Person> getList(@Param("name2") String name,@Param("age") String age);@SelectProvider(type = UserDaoProvider.class, method = "getPersonByPara")Person getPersonByPara(@Param("name")  String name, @Param("age") String age);
}

如上面所示

调用的时候就直接写

        PersonMapper personMapper = session.getMapper(PersonMapper.class);Person result = personMapper.getPersonByPara("渭水","");List<Person> list = personMapper.getList(null,"25");

其实,这就回到我在序言里说的了,在java代码里写脚本语言很丑,代码很难读。

我们可以用一个单独的java方法来产生sql语句,如下;

@Mapper
public interface PersonMapper {@SelectProvider(type = UserDaoProvider.class, method = "getPersonByPara")Person getPersonByPara(@Param("name")  String name, @Param("age") String age);
}public class UserDaoProvider {public String getPersonByPara(Map<String, Object> para) {String sql = new SQL() {{SELECT("id, name, age");FROM("person");if (StringUtils.isNotBlank("" + para.get("name"))) {WHERE("name='" + para.get("name")+"'");}if (StringUtils.isNotBlank("" + para.get("age"))) {WHERE("age=" + para.get("age"));}}}.toString();System.out.println("----" + sql);return sql;}
}

Mybatis与spring

通过上面我们可以看到,不管是用xml配置,还是直接在java文件里写注解,最终都是通过SqlSession 来指向下一步的操作的。

所以,就算我们把mybatis与spring整合,其实核心原理还是依赖SqlSession 的。

第一步,我们先去掉之前的mybatis依赖,改成

<dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.6</version>
</dependency>

然后在spring的xml里面加上

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" />
</bean>

dataSource的相关代码我这边就赘述。

然后,具体来说,整合的思路有两种:

MapperFactoryBean

把我们的mapper放到MapperFactoryBean里面

@Mapper
public interface UserMapper {@Select("SELECT * FROM users WHERE id = #{userId}")User getUser(@Param("userId") String userId);
}
<bean id="myUserMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"><property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" /><property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

使用的时候

@service
public class FooServiceImpl implements FooService {@Resource // 注意这里找的是一个 beanId为myUserMapper的beanprivate final UserMapper myUserMapper;public FooServiceImpl(UserMapper userMapper) {this.userMapper = userMapper;}public User doSomeBusinessStuff(String userId) {return this.userMapper.getUser(userId);}
}

我们怎么说也是写了年代码的人了,稍微往下追一下吧

先说userMapper它最后是MapperFactoryBean,哦,FactoryBean,来大家看一下:

Spring揭秘 读书笔记 三 bean的scope与FactoryBean_程序员小董的专栏-CSDN博客,也就是说最终userMapper是MapperFactoryBean调用getObject的结果,我们看看代码

public abstract class SqlSessionDaoSupport extends DaoSupport {private SqlSessionTemplate sqlSessionTemplate;public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);}}protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);}public SqlSession getSqlSession() {return this.sqlSessionTemplate;}}public class SqlSessionTemplate implements SqlSession, DisposableBean {
//......
}

哦原来SqlSessionTemplate 本来就是SqlSession哦

SqlSessionDaoSupport 

第二种方法是SqlSessionDaoSupport 

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {public User getUser(String userId) {return getSqlSession().selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);}
}

然后把sqlSessionFactory注入进去就好

<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl"><property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

其实两个方法也差不多,

那我现在又有另一个mapper了,怎么办?再写一遍下面的代码么?

<bean id="userMapper2" class="org.mybatis.spring.mapper.MapperFactoryBean"><property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" /><property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

不是这样的,我们有三种方案:

 
<!-- 方案1 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="org.mybatis.spring.sample.mapper" />
</bean><!-- 方案2 --><beans xmlns=""xmlns:xsi=""xmlns:mybatis=""xsi:schemaLocation=" .xsd .xsd"><mybatis:scan base-package="org.mybatis.spring.sample.mapper" /></beans><!-- 方案3 -->
@Configuration
@MapperScan("org.mybatis.spring.sample.mapper")
public class AppConfig {// ...
}

Mybatis与springBoot

最简单的SpringBoot整合MyBatis教程_江南一点雨的专栏-CSDN博客_springboot整合mybatis步骤

Mybatis与mybatis-plus

Redirect

参考资料

mybatis-spring –

更多推荐

聊聊MyBatis的历史

本文发布于:2024-02-06 03:30:49,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1746271.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:历史   MyBatis

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!