7、JavaWeb-Mybatis基础操作
P123 Mybatis-基础操作-删除
点击删除,前端传入选中内容的唯一标识,即主键ID,然后后端根据主键进行删除。
delete from emp where id = 17
。
假设还是使用注解执行SQL语句,则有:
@Mapper
public interface EmpMapper {
// 根据id删除数据
@Delete("delete from emp where id = 17")
public void delete();
}
如何将需要删除的id动态的传递到sql语句中呢?
可以在调用mapper接口方法的时候传递参数,mybatis中提供的占位符为#{},如果mapper接口方法形参只有一个普通类型的参数,#{}里面的属性名可以随便写
进行单元测试:
// 声明mapper
@Autowired
private EmpMapper empMapper; // 使用bean
@Test
public void testDelete(){
empMapper.delete(17);
System.out.println("删除成功");
}
运行单元测试,可以成功删除。
注意Delete操作有返回值,返回影响的记录数,其余增加修改也是。
P124 Mybatis-基础操作-删除(预编译SQL)
可以在application.properties中打开mybatis的日志,并指定输出到控制台。
#配置mybatis的日志信息,指定输出到控制台,通过关键字联想
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
再执行单元测试,可以看到日志中:
上图中所示的SQL语句称为预编译SQL,
优势
-
性能更高
-
更安全,防止SQL注入
下面图理解预编译SQL优势,性能更高
SQL注入:通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。
例如用户登录输账号密码,则在数据库执行查找的sql语句为:
select count(*) from emp where username = ' ' and password = ' '
,直接将输入的账号和密码拼接在sql语句当中,
sql注入则类似下图所示,账号随便写,然后密码写'or'1'='1
,
这样的sql语句造成的情况就是1=1永远是True的,所以用户可以登录成功。
所以解决SQL注入就不能通过这种直接拼接的形式,而是采用预编译的形式,使用java -jar 名称
运行jar包。采用预编译是使用?进行占位的,这样就避免了sql注入问题。
使用Mybatis框架,怎么选择我们采用sql预编译的操作呢?使用#{}的占位符,生成的就是预编译sql语句。
参数占位符:
-
#{},执行sql的时候会将其替换为?,生成预编译SQL,会自动设置参数值,使用场景:参数传递都是用#{}
-
${},拼接SQL,直接将参数拼接在SQL语句中,存在SQL注入问题,使用场景:如果对表名、列表进行动态设置时使用。
P125 Mybatis-基础操作-新增
插入的sql示例:insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) value('Tom', '汤姆', 1, '1.jpg', 1, '2005-10-01', 1, now(), now())
在参数传递中,多个参数推荐直接封装到对象当中,调用insert方法只需要传递一个对象即可,
所以mapper中Insert如下所示:用的?占位,避免了sql注入,
// 新增员工的操作
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) value(#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
public Integer insert(Emp emp);
然后进行单元测试,
// 新增员工测试
@Test
public void testInsert(){
// 构建员工对象
Emp emp = new Emp();
emp.setUsername("Joke");
emp.setName("爵克");
emp.setImage("1.jpg");
emp.setGender(1);
emp.setJob((short)1);
emp.setEntryDate(LocalDate.of(2000, 12, 21));
emp.setDeptId(1);
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
Integer num = empMapper.insert(emp);
System.out.println("新增成功: " + num);
}
P126 Mybatis-基础操作-新增(主键返回)
主键返回:意思就是在数据添加成功后,需要获取插入数据库数据的主键,例如添加套餐数据时,还要维护套餐菜品关系表结构。
如果执行insert操作后,获取主键,返回为null,说明没有拿到
Integer num = empMapper.insert(emp);
// 保存后尝试获取保存的主键值
System.out.println(emp.getId());
如果要拿到主键值,可以再在insert方法上加上一个注解@options(keyProperty="id", useGeneratedKeys=true)
,useGeneratedKeys
为true代表需要拿到生成的主键值,keyProperty="id"
代表最终获取主键会封装到id中。
然后进行单元测试,可以获取到返回的主键值。
P127 Mybatis-基础操作-更新
编辑操作:先查询某条数据,用于页面回显展示,然后编辑数据更新数据库
一般都是根据主键修改员工信息。
mapper部分:
// 更新操作
@Update("update emp set username= #{username}, name=#{name}, gender=#{gender}, image=#{image}, job=#{job}, entrydate=#{entryDate}, dept_id=#{deptId},update_time=#{updateTime} where id = #{id}")
public Integer update(Emp emp);
进行测试可以实现更新操作。
P128 Mybatis-基础操作-查询(根据id查询)
同样的原理即可。
数据封装:
-
实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装
-
如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装
例如执行Emp emp = empMapper.getById(21);
这个操作后字段名和实体类属性名不一致则不能自动封装,所以进行解决:
-
方法一:为字段名起别名,例如
select dept_id deptId
-
方法二:通过@Result注解手动映射封装,column是数据库字段名,property是类中的属性名,
// 方式二:result注解
@Results({
@Result(column = "dept_id", property = "deptId"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
@Select("select * from emp where id = #{id}")
public Emp getById(Integer id);
- (推荐)方法三:开启mybatis的驼峰命名自动映射开关,在properties中进行配置,
# mybatis驼峰命名映射 mybatis.configuration.map-underscore-to-camel-case=true
使用第三方法,应该遵守数据表中字段名是_分隔的,类中属性名是驼峰命名的,才能自动映射
P129 Mybatis-基础操作-查询(条件查询)
按条件查询,支持模糊查询,则sql语句可以是:注意模糊匹配使用%%
// 条件查询员工信息,多条员工信息需要进行封装
@Select("select * from emp where name like '%${name}%' " +
"and gender = #{gender} and entrydate between #{begin} and #{end} " +
"order by update_time desc")
public List<Emp> list(String name, Integer gender, LocalDate begin, LocalDate end);
而模糊匹配那里由于?不能在引号内,使用了${},但是这样会有sql注入的影响,因此解决办法是使用concat方法进行拼接,就变为了like concat('%', #{},'%')
P130 Mybatis-XML映射文件
回顾mybatis配置sql语句的两种方式
-
注解
-
xml
xml映射文件的名称:
-
xml映射文件的名称要与mapper接口名称一致,并且将xml映射文件和mapper接口放置在相同包下面
-
Xml映射文件的namespace属性为mapper接口全类名一致
-
xml映射文件中sql语句的id与mapper接口中的方法名一致,并保持返回类型一致。
在resource中创建包结构,要按照目录层级进行创建,com/it/heima。
xml的约束直接查阅官方文档,
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
根标签mapper,mapper标签有一个唯一的属性namespace,需要与mapper接口的全类名保持一致,获取全类名,在接口类名中选择copy reference即可。
然后在映射文件中定义sql语句,而在mapper中就可以只定义方法,可以不用注解了。
注意sql的id与方法名一致。
resultType为单条记录所封装的类型。
现在理解一下调用mapper的方法是怎么实现sql的,实际上就是依据与当前mapper相同的xml文件,然后又找到与当前方法相同的sql的id,然后就可以执行sql语句了,完成了功能。
插件:mybatisx,提高mybatis的开发效率,安装成功后可以看到,empmapper和对应的xml文件都有了小鸟一样,可以实现两者的快速定位。
最后,简单一点的使用注解映射会使代码简洁,但是复杂一点的操作,建议使用XML映射。
P131 Mybatis-动态SQL-if
动态SQL:随着用户的输入或外部条件的变化而变化的SQL语句
例如多条件查询,有些条件不传入有些条件传入,这个时候就不能把sql写死了,需要动态sql
更改后的xml内容:其中里面写的是sql语句,使用了判断条件,条件符合则将其拼接到sql语句中,反之则不。
<select id="list" resultType="com.hongdou.entity.Emp">
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp
where
<if test="name != null">
name like concat('%', #{name}, '%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
</select>
注意test里面的判断的是类属性名,#{}中也是类属性名
动态sql中又提供了一个标签,替代sql语句中的where关键字。
使用将所有的查询包裹起来,有两个作用:
-
根据里面的指标动态判断条件,如果都不成立则不会生成where
-
会自动去除掉条件前面多余的and 或者or,避免了
where and gender = 1
的情况发生。
P132 Mybatis-动态SQL-if-案例
分析:采用之前的注解来更新某一条信息,则下面写的固定的字段每一次都要进行更新,如果有些字段没有传入更新的值则会被更新为null。
@Update("update emp set username= #{username}, name=#{name}, gender=#{gender}, image=#{image}, job=#{job}, entrydate=#{entryDate}, dept_id=#{deptId},update_time=#{updateTime} where id = #{id}")
public Integer update(Emp emp);
因此动态传递更新,即更新时传递有值,则动态更新,没有值则不更新,用到动态SQL。
例如mapper中定义了方法update2,点击alt+enter,选择generate statement回车,就可以在xml中直接生成了标签。
在update语句中使用标签可以将所有字段进行包裹,同时也会去除掉sql中多余的逗号。
<!-- 完成动态更新操作-->
<update id="update2">
update emp
<set>
<if test="username != null">username= #{username},</if>
<if test="name != null">name=#{name},</if>
<if test="gender != null">gender=#{gender},</if>
<if test="image != null">image=#{image},</if>
<if test="job != null">job=#{job},</if>
<if test="entryDate != null">entrydate=#{entryDate},</if>
<if test="deptId != null">dept_id=#{deptId},</if>
<if test="updateTime != null">update_time=#{updateTime}</if>
</set>
where id = #{id}
</update>
P133 Mybatis-动态SQL-foreach
假设是批量删除的操作,则sql语句为:delete from emp where id in(18,19,20);
先在mapper中定义批量删除方法,可用集合存储批量删除的ids,如下所示:
public void deleteByIds(List<Integer> ids);
而在xml中需要遍历集合中的id,然后进行拼接,
介绍一下标签循环遍历操作,其中常见属性:
-
collection:要遍历的集合
-
item:遍历的元素
-
separator:分隔符
-
open:遍历开始前拼接的sql片段
-
close:遍历结束后拼接的sql片段
示例:
<!-- 完成批量删除信息的sql, 例如集合id in (18,19,20),所以分隔符是,-->
<delete id="deleteByIds">
delete from emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
删除的单元测试如下所示:
// 批量删除的测试类
@Test
public void testDeleteByIds(){
List<Integer> ids = Arrays.asList(13, 14, 15);
empMapper.deleteByIds(ids);
}
P134 Mybatis-动态SQL-sql&include
使用select * 性能比较低并不推荐。假设是下面的两个查询,查询的字段都是相同的,如果多个这种情况,怎么提高代码的复用性呢?
标签,可以抽取出查询字段并定义一个唯一标识id属性,则可以将这个片段引入进来。
例如:
<sql id="commonSelect">
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp
</sql>
// 在使用的地点
<include refid="commonSelect"></include>
原文地址:https://blog.csdn.net/Misnearch/article/details/136469200
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!