[Spring] MyBatis操作数据库(进阶)
🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:
🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀线程与网络(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
目录
- 1. 数据库连接池
- 1.1 为什么会有连接池
- 1.2 常用的数据库连接池
- 2. 动态SQL
- 2.1 ``<if>``标签
- 2.2 ``<trim>``标签
- 2.3 ``<where>``标签
- 2.4 ``<set>``标签
- 2.5 ``<for each>``标签
- 2.6 ``<include>``标签
- 3. 案例练习(表白墙)
- 3.1 配置文件
- 3.2 Mapper层代码
- 3.3 Service层代码
- 3.4 Controller层代码
1. 数据库连接池
1.1 为什么会有连接池
数据库连接池技术也是一种池化技术,他与线程池类似,在通过Spring连接数据库的时候,避免了频繁的创建和销毁与数据库的连接.保证了资源的合理利用.
没有使用数据库连接池的情况: 在每一次在Spring中执行SQL语句的时候,都会先创建一个与数据库的连接对象,在执行完SQL语句之后,再关闭SQL语句,这种重复创建和销毁对象的行为比较浪费系统资源.
使用数据连接池的情况: 在程序启动的时候,会在连接池中创建Collection对象,在执行SQL语句的时候,会在连接池中申请一个连接对象,在执行SQL语句之后,又会把线程归还给连接池.
1.2 常用的数据库连接池
• C3P0
• DBCP
• Druid
• Hikari
目前比较流行的是Hikari,Druid,其中,Druid是阿里巴巴开源项目中的连接池.
其中Hikari是一个Spring程序中默认使用的连接池.如果想把连接池切换为Druid,我们需要在pom文件中引入依赖:
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-3-starter</artifactId><version>1.2.21</version>
</dependency>
2. 动态SQL
动态SQL可以完成不同场景下的SQL拼接.
2.1 <if>
标签
在我们注册用户的时候,我们有一个这样的问题,有时候有的选项不是必填项.在添加用户的时候SQL字段个数是不确定的.这时候就需要我们使用<if>
标签了.比如gender性别是非必填字段:
接口定义:
public Integer insertUser2(UserInfo userInfo);
sql:
<insert id="insertUser2">insert into userinfo (id,username,password,age,<if test="gender != null">gender,</if>phone) values (#{id},#{username},#{password},#{age},<if test="gender != null">#{gender},</if>#{phone})
</insert>
或者我们也可以使用注解的方式,使用<script></script>
标签把上面的xml语句括起来即可.但是我们不推荐用这种做法.
@Insert("<script>" +"INSERT INTO userinfo (username,`password`,age," +"<if test='gender!=null'>gender,</if>" +"phone)" +"VALUES(#{username},#{age}," +"<if test='gender!=null'>#{gender},</if>" +"#{phone})"+"</script>")
public Integer insertUser2(UserInfo userInfo);
注意if中的gender是对象的属性,不是SQL的字段,因为对象是从前端传过来的,我们在判断的时候,是判断前端有没有传值过来,而前段的值就是传到后端的方法参数中的.
2.2 <trim>
标签
上面只使用if标签有一定的问题.要是我们在拼接SQL语句的时候,if标签在最后一个字段,比如:
<insert id="insertUser2">insert into userinfo (id,username,password,age,gender,<if test="phone != null">phone</if>) values (#{id},#{username},#{password},#{age},#{gender},<if test="phone != null">#{phone}</if>)
</insert>
当我们没有phone属性的时候,我们在gender字段后面就会多上一个逗号,这显然不符合sql的语法,所以我们就需要使用<trim>
标签.
标签中有如下属性:
• prefix:表示整个语句块,以prefix的值作为前缀
• suffix:表示整个语句块,以suffix的值作为后缀
• prefixOverrides:表示整个语句块要去除掉的前缀
• suffixOverrides:表示整个语句块要去除掉的后缀
调整上面的插入语句:
<insert id="insertUser3">insert into userinfo<trim prefix="(" suffix=")" suffixOverrides="," prefixOverrides=",">id,username,password,age,gender,<if test="phone != null">phone</if></trim>values<trim prefix="(" prefixOverrides="," suffix=")" suffixOverrides=",">#{id},#{username},#{password},#{age},#{gender},<if test="phone != null">#{phone}</if></trim>
</insert>
假如所有的字段都是可选字段:
<insert id="insertUser4">insert into userinfo<trim prefixOverrides="," suffixOverrides="," suffix=")" prefix="("><if test="id != null">id,</if><if test="username != null">username,</if><if test="password != null">password,</if><if test="age != null">age,</if><if test="phone != null">phone</if></trim>values<trim prefix="(" suffix=")" suffixOverrides="," prefixOverrides=","><if test="id != null">#{id},</if><if test="username != null">#{username},</if><if test="password != null">#{password},</if><if test="age != null">#{age},</if><if test="gender != null">#{gender},</if><if test="phone != null">#{phone}</if></trim>
</insert>
trim标签中的参数的用意就是:在前面和后面只要有逗号,就都会去掉,之后再在sql语句中的前后拼接上括号.
这个标签就是把<trim>
中的内容当做一个字符串,对这个字符串进行整体加减前后缀.
在注解中写的方法和上面的if标签是一样的,这里不再赘述.
2.3 <where>
标签
下面这个场景,系统会根据我们的筛选条件,动态组装where条件.
这样的场景该如何实现呢?下面我们来看例子.
传入的用户对象,根据属性做where条件查询,用户对象中属性不为null的,都为查询条件.
接口定义:
public List<UserInfo> selectUser1(UserInfo userInfo);
根据传过来的对象限制查询条件.
sql:
<select id="selectUser1" resultType="com.jrj.mybatis.UserInfo">select * from userinfo<where><if test="gender != null">#{gender},</if><if test="age != null">#{age},</if><if test="deleteFlag != null">#{deleteFlag}</if></where>
</select>
<where>
标签和<trim>
标签类似,where可以去除掉前后的and和or,如果where后面没有字段的话,就没有where语句了,where关键字不予保留.效果和<trim prefix="where" prefixOverrides="and">
相同,但是使用这个标签的时候,当没有限制条件的时候,where关键字就会保留下来.
2.4 <set>
标签
根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容.
方法接口:
public Integer updateUser1(UserInfo userInfo);
sql:
<update id="updateUser1">update userinfo<set><if test="username != null">username = #{username},</if><if test="age != null">age = #{age},</if><if test="gender != null">gender = #{gender}</if></set> where id = #{id}
</update>
<set>
标签和上面的<where>
标签一样,会自动去除逗号,也会根据是否有更新字段来判断是否要添加set关键字.与<trim prefix="set" suffixOverrides=",">
效果一样,但是在没有传入跟新字段的时候,不会去掉set关键字.
2.5 <for each>
标签
对集合进行遍历的时候,可以用这个标签,一般与sql语句中的in配合使用.
对集合进行遍历时可以使用该标签。标签有如下属性:
• collection:绑定方法参数中的集合,如List,Set,Map或数组对象
• item:遍历时的每⼀个对象
• open:语句块开头的字符串
• close:语句块结束的字符串
• separator:每次遍历之间间隔的字符串
比如根据多个用户id删除用户数据.
接口:
public Integer updateDeleteFlag1(List<UserInfo> userList);
sql:
<update id="updateDeleteFlag1">update userinfo set delet_flagwhere id in<foreach collection="userList" item="id" open="(" close=")">#{id}</foreach>
</update>
这里我们在删除用户数据的时候,使用的是逻辑删除的方式:
删除方式一共有两种,一种是逻辑删除,一种是物理删除,物理删除就是使用Delete语句,在计算机的存储空间中彻底删除,想要回复只能通过日志回滚的方式进行恢复,非常麻烦.而逻辑删除就是把表中每个数据的必备字段,delete_flag标记为无效的数值,并没有从硬盘上真正删除掉,恢复的时候只需要把这个数值改成有效数值即可.我们在企业开发的时候,一般用的都是逻辑删除.
[补充] 其中的collection参数也可以用list1,list2来表示,和前面的方法形参与sql语句传参类似.
2.6 <include>
标签
这个标签通常与<sql>
标签配合使用,在xml文件中,有一些sql语句是赘余的,即sql语句中的赘余片段.
我们可以对重复的代码片段段进行抽取,将其通过 <sql>
标签封装到⼀个SQL片段.之后在每一个sql语句中使用<include>
标签进行引用即可.
<sql>
: 对可以重复引用的sql语句进行定义,在参数id中定义该sql片段的名字.<include>
: 通过属性refid,指定引用的sql片段.
<sql id="selectAllColumn">id,username,password,age,gender,phone,delete_flag,create_time,update_time
</sql><select id="selectAllUser2" resultType="com.jrj.mybatis.UserInfo">select<include refid="selectAllColumn"></include>from userinfo
</select>
3. 案例练习(表白墙)
前面我们写了表白墙,但是在服务器重启之后,就会清空之前的数据,数据无法永久保留,这时候就需要引入MyBatis了,我们需要把数据存储在数据库中.
3.1 配置文件
spring:application:name: vindicatewall# 数据库连接配置datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: qwe123524driver-class-name: com.mysql.cj.jdbc.Drivermybatis:configuration: # 配置打印 MyBatis⽇志log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true #配置驼峰⾃动转换
需要配置的选项有这几个,第一个是数据库相关配置,包括数据库url,用户名,密码,MySQl驱动类.其次,需要配置MyBatis中的日志打印和驼峰自动转换.
3.2 Mapper层代码
@Mapper
public interface MessageMapper {@Insert("insert into message_info (`from`,`to`,`message`) values " +"(#{from},#{to},#{message})")public Integer updateMessage(Message message);@Select("select * from message_info")public List<Message> selectAllMessage();
}
3.3 Service层代码
@Service
public class MessageService {@Autowiredpublic MessageMapper messageMapper;public Integer updateMessage(Message message){return messageMapper.updateMessage(message);}public List<Message> selectAllMessage(){return messageMapper.selectAllMessage();}
}
3.4 Controller层代码
@RestController
@RequestMapping("/messagewall")
public class MessageController {@Autowiredpublic MessageService messageService;@RequestMapping("/publish")public Boolean updateMessage(Message message){if (StringUtils.hasLength(message.getFrom())&&StringUtils.hasLength(message.getTo())&&StringUtils.hasLength(message.getMessage())){messageService.updateMessage(message);return true;}return false;}@RequestMapping("/getlist")public List<Message> selectAllMessage(){return messageService.selectAllMessage();}
}
Postman测试后端代码:
数据库已更新: