ssm-mybaties-学习笔记
1.ibatis的方式和原理
1.同样在书写一个配置类,进行sql语句的书写,不过与mybatis不同的是不需要严格规定namespace 和id的字符串内容
2.在该图片中我们可以发现,ibatis是直接使用它内置的接口比如如图所示的select接口去查询,而不是想mybatis一样去调用代理类的方法
大致工作原理:通过在mapper的配置文件中书写对应的sql语句
接着通过config文件读取mapper文件,使其能够被Java代码读取
接着我们通过factory类里面创建一个sqlsession类进行查找,使用对应的mapper里面的namespace查找唯一对应的sql语句,当sql语句查询到数据之后返回给java代码传送给sqlsession对象
2.mybatis基于ibatis的工作原理
如图所示,mybatis会通过获取getmapper获取mapper接口的一个反射创建一个代理对象,
此时就相当于获取了其mapper的全限定符
通过这个代理对象(jdk动态代理生成)将类的全限定符获取,然后获取方法名,将其拼接成为一个字符串去mapper文件里面查找
由1中介绍的ibatis的工作原理和方法,我们就可以知道,ibatis的查找sql语句是通过获取namespace+数据库操作id定位到sql语句实现对数据库数据的获取的
那么观察mybatis的方法,我们就可以发现如上图所示的一样
1.先通过mapper获取一个代理对象,获取到mapper的一个反射,即xml文件中namesapce填写的是mapper类的全限定符
2.接着,再通过代理对象调用方法,获取到方法名
3.此时mybatis的源码就会将反射获取的全限定符和方法名转换为一个字符串拼接在一起
对应了xml文件中的namspace+数据库操作id 这里其实就是和ibatis中的操作是一样的了
这时候通过源码我们就能够发现,源码先是判断你的操作是什么类型
紧接着再将你传入的参数封装成一个param对象传入selectone方法中去获取数据库操作
再数据库操作中,获取类全限定符和方法名拼接在一起去获取xml文件中的对应的sql语句,这时候你会发现,他还是调用的ibatis的selectOne方法,只不过进行了一层封装罢了
3.mybatis文档使用
1.mybatis.xml配置文件的书写
1.configuration标签里面的重点
1.environments标签(环境配置)重点!!
如图所示,红色箭头所指,再enviroments标签里,default代表着那个环境配置的生效。如蓝绿箭头所示,enviroments里面又会存在多个environment每个environment又有一个唯一标识的id,再default里面去填写对应环境配置的id就会使得对应的环境配置生效
接下来观察里面的各个标签
transactionManager标签(事务管理器)
只有 JDBC和MANGER两种值
1.jdbc:会自动地提交事务已经事务的回滚,不需要我们进行操作
2.manger:需要我们手动进行提交
dataSource标签(数据库源)
只有 POOLED 和 UNPOOLED’
pooled:mybatis自动帮我们维护一个连接池
unpooled:每次进行数据库操作时都要新建或者释放连接
setting标签
设置mybatis的日志开关,能够告诉你的sql语句到底是怎么拼接的
这样就能够实现mybatis的日志输出
typeAliases标签
能够将返回值类型里面的全限定符进行起别名简化,注释中有写使用的方法和作用
<typeAliases>
<!-- 方式1:直接用类的全限定符取别名-->
<typeAlias type="com.atguigu.pojo.Employee" alias="ergouzi"/>
<!-- 方式2:批量将包下的类名给与别名,别名就是类的首字母小写后的类名-->
<package name="com.atguigu.pojo"/>
</typeAliases>
Mapper(映射器)
这个标签的主要功能是告诉mybaytis sql语句要去哪里寻找
数据输入和获取
一般的模板如下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<!--
两种方式对方法中的变量进行引用
#{} : 占位符 + 赋值 emp_id=? ? = 赋值
${} : 字符串拼接 “emp_id=” + id
推荐使用#{key} 防止【注入攻击】的问题
-->
<mapper namespace="com.atguigu.mapper.EmployeeMapper">
<select id="queryById" resultType="com.atguigu.pojo.Employee">
select emp_id empId, emp_name empName , emp_salary empSalary
from e_tmp where emp_id
</select>
</mapper>
两种变量赋值的说明
简单来说就是$符号进行的赋值的优势是动态给列名赋值
#的优势是能够防止注入攻击
当sql传入的是单个简单类型数据时
只需如图所示,当传入的是单个简单数据类型时,只需要用#号键包裹一个名称即可,这个名称可以随便取
当sql需要传入多个简单数据类型时
我们有如下接口,里面需要传入两个简单参数,那么我们该如何传入呢,相较于单个简单类型的随便取key值,多个简单数据就复杂了点
目前提供了两种方法解决
1.使用注解
我们只需要在接口上添加一个如这样的方法即可在sql语句中使用注解中提供的方法名称添加对应的参数
2.使用mybatis机制
接口中的参数从左至右 依次填入arg0 arg1
或者是param1 param2
当sql传入一个map类型的数据时
只需要将传入的参数名字和map里面的key值相同就行了
当sql需要传入一个实体类的时候的做法
如图所示,有一个接口需要传入一个Employee类型的实体类
此时sql语句的书写该如何进行
、
如图所示,红色箭头所指的地方是传入sql中的数据的名称,该名称应该要和实体类中的属性名相同,即黄色箭头所指的地方
这样就能够读取传入的实体类中存储的参数
其原理是mybatis会通过反射调用类的getXxx setXxx方法去获取类的参数,然后将其赋值
数据输出
数据输出总体上有两种形式:
- 增删改操作返回的受影响行数:直接使用 int 或 long 类型接收即可
- 查询操作的查询结果
我们需要做的是,指定查询的输出数据类型即可!
并且插入场景下,实现主键数据回显示!
单个简单类型
细节解释
select标签,通过resultType指定查询返回值类型!
resultType = "全限定符 | 别名 | 如果是返回集合类型,写范型类型即可"
<select id="selectEmpCount" resultType="int">
select count(*) from t_emp
</select>
别名问题:
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
</typeAliases>
当这样配置时,`Blog` 可以用在任何使用 `domain.blog.Blog` 的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases> <package name="domain.blog"/> </typeAliases>
每一个在包 `domain.blog` 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 `domain.blog.Author` 的别名为 `author`;若有注解,则别名为其注解值。见下面的例子:
@Alias("author")
public class Author {
...
}
下面是Mybatis为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
|别名|映射的类型|
|-|-|
|_byte|byte|
|_char (since 3.5.10)|char|
|_character (since 3.5.10)|char|
|_long|long|
|_short|short|
|_int|int|
|_integer|int|
|_double|double|
|_float|float|
|_boolean|boolean|
|string|String|
|byte|Byte|
|char (since 3.5.10)|Character|
|character (since 3.5.10)|Character|
|long|Long|
|short|Short|
|int|Integer|
|integer|Integer|
|double|Double|
|float|Float|
|boolean|Boolean|
|date|Date|
|decimal|BigDecimal|
|bigdecimal|BigDecimal|
|biginteger|BigInteger|
|object|Object|
|object[]|Object[]|
|map|Map|
|hashmap|HashMap|
|list|List|
|arraylist|ArrayList|
|collection|Collection|
返回map类型
java接口
Map<String,Object> selectEmpNameAndMaxSalary();
sql语句
<!-- Map<String,Object> selectEmpNameAndMaxSalary(); -->
<!-- 返回工资最高的员工的姓名和他的工资 -->
<select id="selectEmpNameAndMaxSalary" resultType="map">
SELECT
emp_name 员工姓名,
emp_salary 员工工资,
(SELECT AVG(emp_salary) FROM t_emp) 部门平均工资
FROM t_emp WHERE emp_salary=(
SELECT MAX(emp_salary) FROM t_emp
)
</select>
返回List集合类型数据
接口
List<Employee> selectAll();
<!-- List<Employee> selectAll(); -->
<select id="selectAll" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary
from t_emp
</select>
对于返回一个集合的查询结果,我们只需要在resultType中指定集合的泛型就可以了
而不需要在resultType里面写一个集合数据类型
究其原因其实是因为ibatis的底层就是用一个List来接收的
主键回显
回显的其意义是获取插入数据的主键
1.自增长主键回显 mysql auto_increment
其xml中的代码如下
useGeneratedKeys 表明我们想要数据库中自动增长的主键的值 keyProperty 接收主键列值的属性是实体类中的谁
<insert id="insertEmp" useGeneratedKeys="true" keyProperty="empId">
insert into t_emp (emp_name,emp_salary)
value(#{empName},#{empSalary});
</insert>
@Test
public void test_01() throws IOException {
// 1.读取外部配置文件
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
// 2.创建sqlsession
SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(stream);
// 3.获取sqlsession对象,他会自动开启事务,但不会自动提交事务,需要你手动进行提交
// 若在openSession(true)这里面写上true那么代表着开启了事务自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 4.获取代理对象
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee=new Employee();
employee.setEmpSalary(888.0);
employee.setEmpName("二狗子");
System.out.println("empId "+employee.getEmpId());
System.out.println("---------------------");
int result= mapper.insertEmp(employee);
System.out.println("empId "+employee.getEmpId());
System.out.println("Row"+result);
// 提交事务和释放资源
sqlSession.commit();//所有dml语句都需要进行事务的提交
sqlSession.close();
}
在Java代码中,我们对获取到的主键进行输出,当插入前输出一次,插入后输出一次
观察输出结果,我们发现,插入前,实体类中的主键值是空值
插入后变成了6,这就是因为xml文件中的那两个标签起了作用
注意!!!
在Java代码中,如果进行dml的sql语句操作,那么需要进行事务的提交
sqlSession.commit();//所有dml语句都需要进行事务的提交
或者是在openSession()方法中将true填入括号,使得事务自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
2.非自增长主键如何维护
我们设置了一个teacher类,其中tid就是非自增长主键
在teacherMapper中有一个insertTeacher接口
在xml文件中,我们调用一个sql语句,通过UUID随机生成一个uid赋予给传入的teacher变量中的tid值,然后再执行sql插入语句插入到数据库中
自定义映射关系
1. 别名对应
将字段的别名设置成和实体类属性一致。
<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 -->
<!-- resultType属性:指定封装查询结果的Java实体类的全类名 -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">
<!-- Mybatis负责把SQL语句中的#{}部分替换成“?”占位符 -->
<!-- 给每一个字段设置一个别名,让别名和Java实体类中属性名一致 -->
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{maomi}
</select>
> 关于实体类属性的约定:
getXxx()方法、setXxx()方法把方法名中的get或set去掉,首字母小写。
2. 全局配置自动识别驼峰式命名规则
在Mybatis全局配置文件加入如下配置:
<!-- 使用settings对Mybatis全局进行设置 -->
<settings>
<!-- 将xxx_xxx这样的列名自动映射到xxXxx这样驼峰式命名的属性名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
SQL语句中可以不使用别名
<!-- Employee selectEmployee(Integer empId); -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}
</select>
3. 使用resultMap
使用resultMap标签定义对应关系,再在后面的SQL语句中引用这个对应关系
<!-- 专门声明一个resultMap设定column到property之间的对应关系 -->
<resultMap id="selectEmployeeByRMResultMap" type="com.atguigu.mybatis.entity.Employee">
<!-- 使用id标签设置主键列和主键属性之间的对应关系 -->
<!-- column属性用于指定字段名;property属性用于指定Java实体类属性名 -->
<id column="emp_id" property="empId"/>
<!-- 使用result标签设置普通字段和Java实体类属性之间的关系 -->
<result column="emp_name" property="empName"/>
<result column="emp_salary" property="empSalary"/>
</resultMap>
<!-- Employee selectEmployeeByRM(Integer empId); -->
<select id="selectEmployeeByRM" resultMap="selectEmployeeByRMResultMap">
select emp_id,emp_name,emp_salary from t_emp where emp_id=#{empId}
</select>
多表查询和映射
什么是多表查询,假设你有一个User表,它里面有一个属性是Employee表里面存放的员工类的数据这叫做多表查询
此时我们需要在mybatis里面使用resultMap进行结果集的自定义映射
实体类设计
当我们对数据库中的多表关联数据进行查询时,我们只需要考虑单向的数据方向即可
比如,数据库中有一个客户表,一个订单表
一个客户对应多个订单,一个订单对应多个客户
在数据库中这个关系叫做一对多关系
但是当我们查询时,只需要考虑查询对象和关联表之间的单向关系然后设计实体类即可
比如查询客户表中的信息时,用户对订单表的关系叫对多
当查询订单表时,订单表对客户的关系叫做对一
对一关系实体类设计
订单对客户的对一关系
我们只需要再订单类中创建一个客户的实体类,然后查询时将客户的数据库信息映射到订单表中的客户变量中即可
public class Customer {
private Integer customerId;
private String customerName;
}
public class Order {
private Integer orderId;
private String orderName;
private Customer customer;// 体现的是对一的关系
}
xml代码:
通过在xml代码里面添加resultMap自定义映射,观察实体类,其中,实体类中,非别的类的属性使用id 和 result 正常映射
当存在别的实体类的时候,使用association标签表明接下来要映射的变量是一个实体类
接着在association里面对该实体类里面的变量再进行映射,这样就可以通过数据库返回的多表查询的数据了
<mapper namespace="com.atguigu.mapper.OrderMapper">
<resultMap id="orderMap" type="order">
<id column="order_id" property="orderName"/>
<result column="order_name" property="orderName"/>
<result column="customer_id" property="customerId"/>
<!-- 给对象属性赋值
property : customer在order表中的属性名
javaType: 对象类型
-->
<association property="customer" javaType="customer">
<id column="customer_id" property="customerId"/>
<result column="customer_name" property="customerName"/>
</association>
</resultMap>
<select id="queryOrderById" resultMap="">
SELECT *
FROM t_order tor
JOIN t_customer tur
ON tor.customer_id = tur.customer_id
WHERE tor.order_id=#{id};
</select>
对多关系实体类设计
在查询类那里用一个列表进行接收
public class Customer {
private Integer customerId;
private String customerName;
private List<Order> orderList;// 体现的是对多的关系
}
public class Order {
private Integer orderId;
private String orderName;
private Customer customer;// 体现的是对一的关系
}
//查询客户和客户对应的订单集合 不要管!
其xml文件书写如下
其中实体类中的List部分使用<collection>标签进行自定义映射
然后在里面对list的泛型类的内部变量进行映射
多表映射总结
在setting标签中,我们可以通过
name="autoMappingBehavior"
标签开启自动的驼峰式命名映射
即如图所示,开启了自动映射之后,我们能够自动映射result(但id主键需要自己进行书写)
三表映射
只需要在resultMap标签中,进行多层的标签嵌套即可
Mybatis动态查询
什么是动态查询?
举个例子,在我们的日常使用中,有些时候我们会通过选择标签进行想要的东西进行筛选
这时候我们就需要动态查询,动态的去给添加一些条件
比如,有人对薪水和工作地点有条件,此时sql语句中需要有对薪水和工作地点进行限制
比如,有人仅仅支队薪水有要求,那么sql语句只需要对薪水进行sql语句的限制即可
在mybatis中提供了强大的标签进行动态的sql语句查询
if和where标签
代码如下所示
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.atguigu.mapper.EmployeeMapper">
<select id="query" resultType="employee">
select * from t_emp
<where>
<if test="name != null">
emp_name=#{name}
</if>
<if test="salary != null and salary > 100">
and emp_salary =#{salary}
</if>
</where>
</select>
</mapper>
如下图的注释所示,解释了各个标签是什么意思
通过添加if标签能够使得当条件传入的参数满足时才会进行sql语句的执行
通过where标签能够使得自动去除多余的and和or
这是什么意思呢,我们假设依旧是用原本的文字叙述的where,当下面的
and emp_salary =#{salary}
生效而上面的
emp_name=#{name}
不生效,此时就会出现多余的where and emp_salary=#{salary} 这种情况
那么where标签就替我们解决了这个问题
set标签
和where标签同理,运用于update的操作中
<!-- void updateEmployeeDynamic(Employee employee) -->
<update id="updateEmployeeDynamic">
update t_emp
<!-- set emp_name=#{empName},emp_salary=#{empSalary} -->
<!-- 使用set标签动态管理set子句,并且动态去掉两端多余的逗号 -->
<set>
<if test="empName != null">
emp_name=#{empName},
</if>
<if test="empSalary < 3000">
emp_salary=#{empSalary},
</if>
</set>
where emp_id=#{empId}
<!--
第一种情况:所有条件都满足 SET emp_name=?, emp_salary=?
第二种情况:部分条件满足 SET emp_salary=?
第三种情况:所有条件都不满足 update t_emp where emp_id=?
没有set子句的update语句会导致SQL语法错误
-->
</update>
trim标签
使用trim标签控制条件部分两端是否包含某些字符
- prefix属性:指定要动态添加的前缀
- suffix属性:指定要动态添加的后缀
- prefixOverrides属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值
- suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值
即如下两图所示,suffixOverrides可以去除跟在语句后面的多余的and
prefixOverrides可以去除后面多余的and和,
相当于自定义where和set标签
choose标签
在多个分支条件中,仅执行一个。
- 从上到下依次执行条件判断
- 遇到的第一个满足条件的分支会被采纳
- 被采纳分支后面的分支都将不被考虑
- 如果所有的when分支都不满足,那么就执行otherwise分支
在下面的语句otherwise时用于where标签不报错,因为where标签需要有一个条件语句,若前面两个都不满足,那么则会触发一个1=1的标签有一个绝对正确的语句
<!-- List<Employee> selectEmployeeByConditionByChoose(Employee employee) -->
<select id="selectEmployeeByConditionByChoose" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_salary from t_emp
where
<choose>
<when test="empName != null">emp_name=#{empName}</when>
<when test="empSalary < 3000">emp_salary < 3000</when>
<otherwise>1=1</otherwise>
</choose>
<!--
第一种情况:第一个when满足条件 where emp_name=?
第二种情况:第二个when满足条件 where emp_salary < 3000
第三种情况:两个when都不满足 where 1=1 执行了otherwise
-->
</select>
sql标签
使用这个标签能够使得重复的sql语句进行复用
foreach查询
批量查询
其中的变量分别代表什么由颜色箭头所指
<select id="queryBatch" resultType="employee">
select * from t_emp
where emp_id in
<!-- 遍历数据
(1,2,3,4)
collection="ids | arg0 | list"
open 遍历之前要追加的字符串 观察上面的1234的括号
close 遍历之后追加的字符串
separator 遍历中间添加的字符
item 每个遍历项
collection 需要进行遍历的id值
(1,2,3,4)
-->
<foreach collection="ids" open="(" close=")" item="id">
<!-- 遍历的内容-->
#{id}
</foreach>
</select>
批量删除
<!-- int deleteBatch(@Param("ids") List<Integer> ids);-->
<select id="deleteBatch">
delete from t_emp where id in
<foreach collection="ids" open="(" separator="," close=")" item="id">
#{id}
</foreach>
</select>
批量插入
<!-- int insertBatch(@Param("list") List<Employee> employeeList);-->
<insert id="insertBatch" >
insert into t_emp (emp_name,emp_salary)
values
<foreach collection="list" separator="," item="employee">
(#{employee.empName},#{employee.empSalary})
</foreach>
<!-- ("xx",1) ,("ss",2)-->
</insert>
批量更新
<!-- int updateBatch(@Param("list") List<Employee> employeeList);-->
<update id="updateBatch">
<foreach collection="list" item="emp">
update t_emp set emp_name = #{emp.empName} , emp_salary=#{empSalary} where emp_id = #{emp.empId};
</foreach>
</update>
批量映射xml文件
在xml文件中,我们通过一个package标签,指定mapper.xml的批量映射
不过注意,其注意事项图片中的注释有写
打包时要与接口的mapper类放在同一个文件夹
所以,我们可以采用一种方法
即将mapperxml文件中的上级目录的文件夹和接口一样就可以了,这样就可以打包到同一位置
注意,创建的时候不能用xx.xx.xx应该是xx/xx/xx/因为xx.xx本质是一个文件夹,但是///是多级文件结构
分页查询插件
1.什么是分页查询
在MySQL中的分页查询中,偏移量(offset)用于指定从哪一条记录开始返回查询结果。分页查询通常用于处理大量数据,以便用户可以在浏览器中逐页查看数据,而不是一次性加载所有数据。
偏移量的概念
- 偏移量(offset):表示从查询结果的开头跳过的记录数。
- 限制(limit):表示返回的记录数。
基本语法
假设你有一个名为
employees
的表,并希望进行分页查询,可以使用如下的 SQL 语句:
sql复制代码
SELECT * FROM employees
ORDER BY some_column
LIMIT pageSize OFFSET offsetValue;
pageSize
:每页显示的记录数。offsetValue
:从查询结果中跳过的记录数。示例
假设你希望每页显示10条记录,并查看第二页的数据:
sql复制代码
SELECT * FROM employees
ORDER BY employee_id
LIMIT 10 OFFSET 10;
在这个例子中:
LIMIT 10
:表示每页返回10条记录。OFFSET 10
:表示跳过前10条记录,从第11条记录开始返回数据。使用场景
分页查询中的偏移量通常在以下场景中使用:
- 大数据集的分页显示:
- 当数据量非常大时,为了优化性能,通常会使用分页查询。
- 用户可以通过点击“下一页”或“上一页”来浏览数据。
- 用户交互体验:
- 在Web应用中,用户可以通过分页导航来查看数据,避免一次性加载过多数据导致页面加载缓慢。
- API设计:
- 在RESTful API中,分页查询是一种常见的做法,通过
page
和size
参数来控制分页。- 例如,
/api/employees?page=2&size=10
可以使用LIMIT 10 OFFSET 10
来实现。注意事项
- 性能问题:
- 当偏移量非常大时,查询性能可能会下降,因为MySQL需要扫描和跳过大量的记录。
- 优化方法包括使用索引、子查询或覆盖索引等技术。
- SQL注入:
- 在处理用户输入的
page
和size
参数时,要注意防止SQL注入攻击。通过使用偏移量,你可以有效地控制分页查询的起始点,从而实现用户友好的数据浏览体验。
如图所示,分页查询中,sql语句会被中途拦截
然后再pageHelper插件的影响下添加分页查询的语句
那么使用插件时,需要到xml文件进行插件的注册
注册完毕之后就能够使用
如图所示的各种数据都是通过分页查询的插件获取到的分页数据
orm思维和逆向工程
orm思维,大致就是统筹面向对象和面向过程的编程语言的一个中间软件
什么是逆向工程?
在插件市场搜索MybatisX插件就可以实现orm自动生成单表实体类和mapper文件
原文地址:https://blog.csdn.net/2302_77493813/article/details/143953132
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!