网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
<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://127.0.0.1:3306/mybatis?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.tianju.dao"/>
</mappers>
log4j.properties文件:
Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
#log4j.rootCategory=debug, CONSOLE, LOGFILE
log4j.rootCategory=debug, CONSOLE
Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=D:/axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
### 3.在resources下建文件
<?xml version="1.0" encoding="UTF-8" ?>
![在这里插入图片描述](https://img-blog.csdnimg.cn/3120bb5a463149b89f9addaacfea6fe9.png)
### 4.在UserMapper中写SQL
要点:
(1)update,delete,insert不要写成select,不然会出现删除数据后,数据库确实删除了,但是前端list还有;
(2)if语句中不要忘记加逗号:
(3)select语句中,要记得加返回值;
## mybatis项目应用
### 1.封装SqlSessionUtils.java工具类
要点:
* 读取mybatis配置文件,告诉那里读取;
* 生成一个建造sqlSession工厂的建造者,SqlSessionFactoryBuilder;
* 建造者SqlSessionFactoryBuilder根据mybatis-config.xml建造一个SqlSession工厂,SqlSessionFactory;
* 在SqlSession工厂中,通过SqlSessionFactory生成一个sqlSession,且打开自动提交sqlSession = sqlSessionFactory.openSession(true);
* 需要什么类型的mapper就提供什么类型的mapper,public static <T> T getMapper(Class<T> mapperClass){return sqlSession.getMapper(mapperClass);};
getMapper方法—泛型T
![在这里插入图片描述](https://img-blog.csdnimg.cn/dd3e93039f5e4a3ba80943dae2208f35.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/a3f2abeb36164d608c7bd8d64f65f6a7.png)
package com.tianju.util;
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;
public class SqlSessionUtils {
private static SqlSession sqlSession;
static {
//1.读取mybatis配置文件
InputStream is = null;
try {
is = Resources.getResourceAsStream(“mybatis-config.xml”);
} catch (IOException e) {
throw new RuntimeException(e);
}
//2.生成一个工厂--这个工程是通过建造者建造出来的
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.建造者根据mybatis-config.xml建造一个SqlSession工厂
SqlSessionFactory sqlSessionFactory = builder.build(is);
//4.通过sqlSessionFactory生成一个sqlSession
sqlSession = sqlSessionFactory.openSession(true);
//如果设置为true,每次自动提交
}
public static <T> T getMapper(Class<T> mapperClass){
return sqlSession.getMapper(mapperClass);
}
}
### 2.多条件查询<where, if>,传入Boolean
![在这里插入图片描述](https://img-blog.csdnimg.cn/1c22d18eec6c4726a9ce0904b03b1963.png)
@Mapper
public interface HouseMapper {
List queryList(
@Param(“buildingId”) Integer buildingId, // 楼栋
@Param(“storey”) Integer storey, // 楼层
@Param(“roomNum”) String roomNum, // 房间号
@Param(“areaMin”) Double areaMin, // 面积
@Param(“areaMax”) Double areaMax, // 面积
@Param(“intoDateBegin”) Date intoDateBegin, // 入住开始时间
@Param(“intoDateEnd”) Date intoDateEnd, // 入住结束时间
@Param(“areaOrderASC”) Boolean areaOrderASC,
@Param(“areaOrderDESC”) Boolean areaOrderDESC// 排序
);
SELECT c_house.id, c_owner_house.ownerId, c_building.num,c_building.floors,c_building.unit, c_house.storey,c_house.roomNum,c_house.area,c_house.into_date,c_house.status,c_house.building_id, user_owner.realname AS ownerName
FROM c_house
LEFT JOIN c_building ON c_building.id = c_house.building_id
LEFT JOIN c_owner_house ON c_owner_house.houseId = c_house.id
LEFT JOIN user_owner ON user_owner.id = c_owner_house.ownerId
<where>
<if test="buildingId!=null">
c_building.id=#{buildingId}
</if>
<if test="storey!=null">
AND c_house.storey=#{storey}
</if>
<if test="areaMin!=null">
AND c_house.area >= #{areaMin}
</if>
<if test="areaMax!=null">
AND c_house.area <= #{areaMax}
</if>
<if test="intoDateBegin!=null">
AND c_house.into_date > #{intoDateBegin}
</if>
<if test="intoDateEnd!=null">
AND c_house.into_date <= #{intoDateEnd}
</if>
<if test="roomNum!=null and roomNum!=''">
AND roomNum LIKE #{roomNum}
</if>
</where>
<if test="areaOrderASC">
ORDER BY c_house.area
</if>
<if test="areaOrderDESC">
ORDER BY c_house.area DESC
</if>
</select>
service层使用
@Autowired
private HouseMapper houseMapper;
@Override
public PageInfo<House> queryList(Integer buildingId, Integer storey, String roomNum, Double areaMin, Double areaMax, Date intoDateBegin, Date intoDateEnd,
Integer pageNum, Integer pageSize,
Boolean areaOrderASC, Boolean areaOrderDESC) {
PageHelper.startPage(pageNum,pageSize);
List<House> list = houseMapper.queryList(buildingId,storey,roomNum,areaMin,areaMax,intoDateBegin,intoDateEnd,areaOrderASC,areaOrderDESC);
return new PageInfo<>(list);
}
### 3.多条件查询IN,foreach+可变长度参数<choose,when,otherwise>,传入list
/\*\*
* 查询空闲的房子,根据buildingId查询,忽略单元
* @param buildingIdList 根据buildingId查询房间,默认查询空间房间,如果status为false,
* 则也可以查询到非空闲的房间
* @param status 默认为true,如果为false,则可以查询到非空闲房子
* @return
*/
List queryHouseByBuildingId(
@Param(“buildingIdList”) List buildingIdList,
@Param(“status”) Boolean… status);
<select id="queryHouseByBuildingId" resultType="house">
SELECT * FROM c_house
WHERE
building_id IN
<foreach collection="buildingIdList" open="(" separator="," close=")" item="id">
#{id}
</foreach>
<choose>
<when test="status!=null and status.length>0 and !status[0]">
</when>
<otherwise>
AND !status
</otherwise>
</choose>
</select>
### 4.多条件查询进阶版<where,choose,when,otherwise,foreach,if>
![在这里插入图片描述](https://img-blog.csdnimg.cn/b415b01821ec4e218eb995ca6fc4cac1.png)
/\*\*
* 查询所有空闲的房子,显示到左边的框里面 TODO:后面再全查询的基础上进行
*/
List queryAllNoIntoHouse(
@Param(“buildingIdList”) List buildingIdList, // 楼栋
@Param(“storey”) Integer storey, // 楼层
@Param(“areaMin”) Double areaMin, // 面积
@Param(“areaMax”) Double areaMax, // 面积
@Param(“areaOrderASC”) Boolean areaOrderASC,
@Param(“areaOrderDESC”) Boolean areaOrderDESC,// 排序
@Param(“status”) Boolean… status // 如果为false,则可以查询到非空闲房子,其他情况 都是查询空闲房子
);
<select id="queryAllNoIntoHouse" resultType="house">
SELECT
c_house.id,
c_owner_house.ownerId,
c_building.num,c_building.floors,c_building.unit,
c_house.storey,c_house.roomNum,c_house.area,c_house.into_date,c_house.status,c_house.building_id,
user_owner.realname AS ownerName
FROM c_house
LEFT JOIN c_building ON c_building.id = c_house.building_id
LEFT JOIN c_owner_house ON c_owner_house.houseId = c_house.id
LEFT JOIN user_owner ON user_owner.id = c_owner_house.ownerId
<where>
<choose>
<when test="buildingIdList!=null and buildingIdList.size()>0">
building_id IN
<foreach collection="buildingIdList" open="(" separator="," close=")" item="id">
#{id}
</foreach>
</when>
<otherwise>
</otherwise>
</choose>
<if test="storey!=null">
AND c_house.storey=#{storey}
</if>
<if test="areaMin!=null">
AND c_house.area >= #{areaMin}
</if>
<if test="areaMax!=null">
AND c_house.area <= #{areaMax}
</if>
<choose>
<when test="status!=null and status.length>0 and !status[0]">
<!-- 如果为false,则可以查询到非空闲房子-->
</when>
<otherwise>
AND !status
</otherwise>
</choose>
</where>
<if test="areaOrderASC">
ORDER BY c_house.area
</if>
<if test="areaOrderDESC">
ORDER BY c_house.area DESC
</if>
</select>
前后端交互controller层
// 2.处理搜索请求
// 默认显示所有可以选择的房间,放到左侧的框里,如果选择,双击,这条数据跳到右边的框里
@RequestMapping(“/query/house”)
@ResponseBody
public ResData queryHouse(
// @RequestParam(value = “buildingList”, defaultValue = “List[]”) List buildingList, // 楼栋
// Integer storey, // 楼层,没有用到
// Double areaMin,
// Double areaMax,
// @RequestParam(value = “areaOrderASC”,defaultValue = “false”) Boolean areaOrderASC,
// @RequestParam(value = “areaOrderDESC”,defaultValue = “false”) Boolean areaOrderDESC
@RequestBody HouseFront houseFront
){
Double areaMax = houseFront.getAreaMax();
Double areaMin = houseFront.getAreaMin();
Boolean areaOrderASC = houseFront.getAreaOrderASC();
Boolean areaOrderDESC = houseFront.getAreaOrderDESC();
Integer storey = houseFront.getStorey();
List buildingList = houseFront.getBuildingList();
List buildingIds = buildingList.stream().map(Building::getId).collect(Collectors.toList());
System.out.println("查询条件:"
+areaMin+"/"
+areaMax+"/"
+buildingIds+"/"
+storey+"/"
);
// 大小的问题
if (!StringUtils.isBlank(areaMin) && !StringUtils.isBlank(areaMax)){
if (areaMin.compareTo(areaMax)>0){
return new ResData(1002, "最小面积不能大于最大面积", null);
}
}
// 这个排序顺序不能两个都是true
if (areaOrderASC && areaOrderDESC){
return new ResData(1003, "排序条件冲突", null);
}
List<House> list = houseService.queryAllNoIntoHouse(buildingIds, storey, areaMin, areaMax, areaOrderASC, areaOrderDESC, true);
list.forEach(System.out::println);
return new ResData(200, "ok", list);
}
前端代码
录入业主基础信息:
用户名: 真实姓名: 电话号码:
身份证号:<input type="text" v-model="identity">
性别:
<input type="radio" v-model="gender" value="男">男
<input type="radio" v-model="gender" value="女">女
<br>
备注信息:<textarea v-model="notes" rows="3" cols="22"></textarea><br>
<br>
录入选房信息:<br>
<div>
楼栋编号:
<select v-model="buildingId">
<option value="">--请选择楼栋--</option>
<option v-for="building in buildingList" :value="building.id">{{building.num}}-{{building.unit}}</option>
</select>
<button @click="addBuildingId">添加楼栋</button>
<button @click="resetSelectBuildsBtn">重选楼栋</button>
<br>
选中的楼栋为:
<select multiple style="width: 100px" @dbclick="removeSelectedBuildingsBtn">
<option v-for="building in selectedBuildingList" :value="building.id">{{building.num}}-{{building.unit}}</option>
</select>
楼层:
<select v-model="selectFloor">
<option value="">-选择楼层--</option>
<option v-for="floors in floorList" :value="floors">{{floors}}层</option>
</select>
面积:
<input type="text" v-model="areaMin" style="width: 50px" placeholder="小">---
<input type="text" v-model="areaMax" style="width: 50px" placeholder="大">
<button @click="areaASC">面积升序</button>
<button @click="areaDESC">面积降序</button>
<button @click="searchHouseBtn">搜索房子</button>
<button @click="searchHouseBtnClr">重置搜索</button><br>
</div>
可选的房子为:
<select multiple style="width: 200px" @dblclick="selectHouseDbc">
<option v-for="house in canSelectHouse" :value="house.id">
{{house.num}}--{{house.unit}}--{{house.roomNum}}--{{house.area}}
</option>
</select>
选中的房子为:
<select multiple style="width: 200px" @dblclick="removeSelectedHouseDbc">
<option v-for="house in selectedHouse" :value="house.id">
{{house.num}}--{{house.unit}}--{{house.roomNum}}--{{house.area}}平米
</option>
</select>
<br>
<button @click="add">添加</button>
<button @click="reset">重置</button><br>
<br>
专门用来前后端交互的实体类
package com.tianju.entity;
import com.alibaba.druid.filter.AutoLoad;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 专门用来和前端交互的一个对象;
* 用于选房页面的复杂查询
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HouseFront {
private List buildingList;
private Integer storey;
private Double areaMin;
private Double areaMax;
private Boolean areaOrderASC;
private Boolean areaOrderDESC;
}
### 5.传入Boolean类型的可变长度参数
/\*\*
* 超级管理员能看离职员工和在职员工的信息,
* 其他员工只能看在职员工信息。
* @param su 表示是否是超级管理员,su 为true 时,为超级管理员
* @return
*/
List queryAllManagerUser(
@Param(“su”) Boolean… su
);
<select id="queryManagerById" resultType="user">
SELECT * FROM
(SELECT
user_manager.id,
user_role.id AS userRoleId,user_role.role_id AS typeId,user_role.user_id AS userId,
user_manager.username,user_manager.password,user_manager.tel,
user_manager.notes AS userNotes,user_manager.create_time,user_manager.update_time,
user_manager.operator,user_manager.status,user_manager.firstLog,
user_role_type.name AS typeName,user_role_type.notes AS typeNotes
FROM user_manager
LEFT JOIN user_role ON user_manager.id = user_role.user_id
LEFT JOIN user_role_type ON user_role.role_id = user_role_type.id
) t
WHERE t.id = #{id}
<!-- 一般人员只能查询在职员工,超级管理员可以查所有员工-->
<choose>
<when test="su==null or su.length < 1">
AND firstLog !='100' AND status
</when>
</choose>
</select>
![在这里插入图片描述](https://img-blog.csdnimg.cn/43d41019692a44a58798838799222e56.png)
### 6.多个条件排序 ORDER BY num,unit
![在这里插入图片描述](https://img-blog.csdnimg.cn/459e5c20d712447cb869a092b496680d.png)
controller层
@RequestMapping("/list")
@ResponseBody
public ResData buildingList(
@RequestParam(value = "pageNum",defaultValue = "1")Integer pageNum,
@RequestParam(value = "pageSize",defaultValue = "10")Integer pageSize,
@RequestParam(value = "order",defaultValue = "false")Boolean order){
PageInfo<Building> pageInfo = buildingService.queryAll(pageSize, pageNum,order);
System.out.println(pageInfo);
return new ResData(200,"ok",pageInfo);
}
service层
@Override
public PageInfo<Building> queryAll(Integer pageSize,Integer pageNum,Boolean order) {
PageHelper.startPage(pageNum, pageSize);
List<Building> list = buildingMapper.queryAll(order);
return new PageInfo<>(list);
}
mapper层
List queryAll(Boolean order);
xml文件的sql
<select id="queryAll" resultType="building">
SELECT * FROM c_building
<if test="order">
ORDER BY num,unit
</if>
</select>
### 7.一条新增影响多个数据库的表@Transactional
![在这里插入图片描述](https://img-blog.csdnimg.cn/e5da64d27aab4bd784ad1dcb130d9016.png)
controller层
// 3.处理新增用户信息
Boolean addFlag = true;
@RequestMapping(“/add”)
@ResponseBody
public ResData addOwner(
@RequestBody Owner owner
){
System.out.println(owner);
// 1.判断前端输入的数据全不
// 2.如果确认不新添房子,则只修改user_owner表,则该用户不能登陆账号
// 3.如果新增了房子,则修改user_owner表,c_house表,c_owner_house表
if (StringUtils.isBlank(owner.getUsername(),owner.getRealname(),owner.getIdentity(),owner.getGender())){
return new ResData(1001, “必填项为空”, null);
}
if (!StringUtils.isLegalPhone(owner.getTel())){
return new ResData(1002, "手机号码不合法", null);
}
// TODO:这样可以吗???
if (addFlag){
if (owner.getHouseList().size()<1){
addFlag = false;
return new ResData(2001, "确定新增一条未选择房间的业主?", null);
}
}
// 初始化一下密码,手机号前4位
owner.setPassword(owner.getTel().substring(0,4));
// 获取session
User user = (User) session.getAttribute("user");
if (user!=null){
owner.setOperator(user.getOperator());
}
Integer flag = ownerService.add(owner);
if (flag<1){
return new ResData(3001, "系统繁忙,请稍后重试", null);
}
addFlag = true; // 新增成功再置为true,下一条就会再次判断是不是没填
return new ResData(200,"新增用户信息成功",null);
}
service层@Transactional
@Transactional
@Override
public Integer add(Owner owner) {
// 如果新用户只是意向客户
if (owner.getHouseList().size()<1){
owner.setHouseNum(0);
return ownerMapper.add(owner);
}
// 买房的客户变成了业主
Integer num = 0; //影响数据库的数据的行数
ownerMapper.add(owner);
for (House house:owner.getHouseList()){
// 房间状态改为入住的状态
houseMapper.intoHouse(house.getId());
// 房屋-业主对应表新增一条数据
houseMapper.addOwnerHouse(owner.getId(), house.getId());
num +=2;
}
return num;
}
<update id="intoHouse">
UPDATE c_house SET into_date = NOW(),status=1 WHERE id=#{id}
</update>
<insert id="addOwnerHouse">
INSERT INTO c_owner_house(ownerId,houseId) VALUES (#{ownerId},#{houseId})
</insert>
## mybatis使用报错集锦
### 1.忘记加 resultType=“opus”
报错信息:
Error querying database. Cause: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement ‘com.tianju.dao.OpusMapper.queryByAuthorIdAndLikeName’. It’s likely that neither a Result Type nor a Result Map was specified.
报错原因:
查询返回值类型没写
![在这里插入图片描述](https://img-blog.csdnimg.cn/a0884b3ffed94fc4beaed7ca2c800f2d.png)
解决方案:
加上resultType=“opus”,如下:
<select id="queryByAuthorIdAndLikeName" resultType="opus">
SELECT t_opus.*,tt.name AS typename,tu.username
FROM t_opus
LEFT JOIN t_types tt ON t_opus.typeId = tt.id
LEFT JOIN user_tab tu ON t_opus.authorId = tu.id
<where>
<if test="authorId!=null">
authorId = #{authorId}
</if>
<if test="name!=null and name!=''">
AND t_opus.name LIKE #{name}
</if>
</where>
ORDER BY t_opus.id DESC
</select>
### 2.前端显示list页面数据为空
问题描述:
pageHelper里面的没有数据,前端显示为空:
PageInfo{pageNum=0, pageSize=0, size=0, startRow=0, endRow=0, total=0, pages=0, list=null, firstPage=0, prePage=0, nextPage=0, lastPage=0, isFirstPage=false, isLastPage=false, hasPreviousPage=false, hasNextPage=false, navigatePages=0, navigatepageNums=null}
原因分析:
pageHelper有顺序要求
![在这里插入图片描述](https://img-blog.csdnimg.cn/4c0d686ac7c54dfcbbf74f8b098bfd59.png)
解决方案:
![在这里插入图片描述](https://img-blog.csdnimg.cn/2c121291a4f342b8927b01eb9d406c13.png)
/\*\*
* 分页在service层完成,利用pageHelper 在service层做完分页的事情
*/
@Override
public PageInfo queryByAuthorIdAndLikeName(String name, Integer authorId, Integer pageNum,Integer pageSize) {
// 先要用pageHelper的 分页,然后执行查询
PageHelper.startPage(pageNum, pageSize);
List list = opusMapper.queryByAuthorIdAndLikeName(name, authorId);
PageInfo pageInfo = new PageInfo<>(list);
return pageInfo;
}
### 3.由于mybatis版本引起的问题
报错信息:
org.apache.ibatis.exceptions.PersistenceException:
Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named ‘username’ in ‘class java.lang.String’
Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named ‘username’ in ‘class java.lang.String’
报错原因
如果3.4.5版本,3.4开头的就是低版本,并且username要在动态sql中使用,即,if,where,中使用,则一个参数也需要加@param,如果是3.5.9以上版本,则可以不加
![在这里插入图片描述](https://img-blog.csdnimg.cn/6d3d330b80374b338dc797d211de70b7.png)
解决方案:
1.UserMapper里面加@param
// 如果3.4.5版本,3.4开头的就是低版本,并且username要在动态sql中使用
// 即,if,where,中使用,则一个参数也需要加@param,如果是3.5.9以上版本,则可以不加
List findByUsernameSQL(@Param(“username”) String username);
2.mybatis版本换成3.5以上
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
在项目中的案例:
![在这里插入图片描述](https://img-blog.csdnimg.cn/a97c3cd2ee77490f9394523a199e153b.png)
接口java文件
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
即,if,where,中使用,则一个参数也需要加@param,如果是3.5.9以上版本,则可以不加
解决方案:
1.UserMapper里面加@param
// 如果3.4.5版本,3.4开头的就是低版本,并且username要在动态sql中使用
// 即,if,where,中使用,则一个参数也需要加@param,如果是3.5.9以上版本,则可以不加
List<User> findByUsernameSQL(@Param("username") String username);
2.mybatis版本换成3.5以上
<!-- mybatis 框架-->
<!-- 小版本会导致userMapper findByUsernameSQL-->
<!-- 3.5.9-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
在项目中的案例:
接口java文件
[外链图片转存中...(img-B3tAmbTl-1715063093188)]
[外链图片转存中...(img-8c87Q1pA-1715063093188)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618608311)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**