5.3 Mybatis基础操作
5.3.1 根据主键删除
-
SQL语句
delete from emp where id = 17;
-
接口方法
@Delete(“delete from emp where id = #{id}”) ->参数占位符
public void delete(Integer id);
-
注意事项:如果mapper接口方法形参只有一个普通类型的参数,#{…}里面的属性名可以随便写,如:#{id}、#{value}。
mybatis日志
#在application.properties中添加如下配置:
#配置mybatis的日志信息,指定输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#我们不需要记住全部字段,只需记住关键字就行。我们在IDEA中输入mybatis.log,IDEA会联想提示。
@Delete("delete from emp where id = #{id}")
public void delete(Integer id);
//我们再次运行上面的方法时:
@Autowired
private EmpMapper empMapper;
@Test
public void testDelete(){
empMapper.delete(16);
}
/*
其中两个==>所在的语句称为预编译SQL。而?是预编译SQL中的参数占位符
*/
预编译SQL
预编译SQL的优势:
-
性能更高
Java执行MySQL语句时需要走四个步骤:SQL语法检查->优化SQL->编译SQL->执行SQL
当我们执行下面的语句时,由于这三句是不同的语句,在SQL中没有缓存,因此上面的四个步骤都要走一边,而且要走三遍
而我们执行下面的语句时:
第一次编译时,SQL会将 delete from emp where id = ?;这句放入缓存中,然后将1以实参的形式进行编译、执行。
但第二次时,由于SQL缓存中有 delete from emp where id = ?;,所以第二次可以直接将2代入执行。第三次也是如此。
所以上面的要编译3次,而下面的只需要编译1次。
-
更安全(防止SQL注入)
SQL注入:通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。
比如我们要登录一个界面,当我们输入正确的账号和密码时:
至于能否登录成功,一般来说只需要账号密码跟服务器里中能查询到数据就行,而如果这种查询语句为:
select count(*) from emp where username = 'zhangwuji' and password = '123456'; -- 只要查询结果不为0,那么就可以登录
然后进行SQL注入操作
-- 如果将密码修改为'or '1'='1 select count(*) from emp where username = 'xxxxx' and password = ''or '1'='1'; -- 那么这条语句将永远正确,也就是能够登录成功,这就是SQL注入
但是如果查询语句改为预编译MySQL
select count(*) from emp where username = ? and password = ?; -- 这里会把输入的内容当作字符串处理,这样就不会修改mysql语句了。也就是可以阻止MySQL注入
-
写出预编译SQL语句,就是用参数占位符实现的。
/* 1.#{...} 执行SQL时,会将#{...}替换成?,生成预编译SQL,会自动设置参数值 使用时机:参数传递,都用#{...} 2.${...} 拼接SQL。直接将参数拼接在SQL语句中,存在SQL注入问题 使用时机:如果对表名、列表进行动态设置时使用(用得比较少)。 两者的区别是,${...}直接将?替换成了实参。#{...}则是间接。 */
5.3.2 新增
-
SQL语句
insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values('伊吕波','168','1','1.jpg','1','2000-01-01',1,now(),now());
-
接口方法
-- empMapper @Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" + " values(#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})") public void insert(Emp emp); -- Test public void testInsert(){ Emp emp = new Emp(); emp.setUsername("伊吕波"); emp.setName("168"); emp.setImage("1.jpg"); emp.setGender(((short)1)); emp.setJob((short) 1); emp.setEntrydate(LocalDate.of(2000,1,1)); emp.setCreateTime(LocalDateTime.now()); emp.setUpdateTime(LocalDateTime.now()); emp.setDeptId(1); empMapper.insert(emp); }
-
控制台
-
emp表
主键返回
- 描述:在数据添加成功之后,需要获取插入数据库数据的主键。如:添加套餐数据时,还需要维护套餐菜品关系表数据。
//在上面的代码基础上,添加下面的注解就可以了。
@Option(keyProperty = 'id',useGeneratedKeys = true)
//会自动将生成的主键值,赋值给emp对象的id属性
//获得并输出id值
System.out.println(emp.getId());
5.3.3 更新
-
SQL语句
update emp set username = '',name = '',gender = '',image = '', job = '',entrydate = '',dept_id = '',update_time = '' where id = x;
-
接口方法
//更新员工
@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 void update(Emp emp);
//更新员工
@Test
public void testUpdate(){
//构造员工对象
Emp emp = new Emp();
emp.setId(18);
emp.setUsername("Tom1");
emp.setName("汤姆1");
emp.setImage("1.jpg");
emp.setGender((short)1);
emp.setJob((short)1);
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(1);
//执行更新员工操作
empMapper.update(emp);
}
5.3.4 查询
根据ID查询
- SQL语句
select * from emp where id = 1;
-
接口方法
//根据ID查询员工 @Select("select * from emp where id = #{id}") public Emp getById(Integer id); //根据ID查询员工 @Test public void testGetById(){ Emp emp = empMapper.getById(20); System.out.println(emp); }
数据封装
- 实体类属性名 和 数据库表查询返回的字段名一致,mybatis会自动封装。否则不会(未封装的字段查询返回null)。
方案
//方案一:给字段起别名,让别名与实体类属性一致
@Select("select id, username, password, name, gender, image, job, entrydate, " +
"dept_id deptId, create_time createTime, update_time updateTime from emp where id = #{id}")
public Emp getById(Integer id);
//方案二: 通过@Results, @Result注解手动映射封装
@Results({
@Result(column = "dept_id", property = "deptId"),
//将字段"dept_id"封装到"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的驼峰命名自动映射开关(可以自动将"a_con"封装成"aCon"),在application.properties中修改配置,推荐用这种。
mybatis.configuration.map-underscore-to-camel-case=true
//然后就可以根据ID查询员工
@Select("select * from emp where id = #{id}")
public Emp getById(Integer id);
条件查询
-
SQL语句
select * from emp where name like '%张%' and gender = 1 and entrydate between '2010-01-01' and '2020-01-01' order by update_time desc;
-
接口方法
//由于查询出的结果可能不止一条,所以这里用List集合封装。另外,“#{}”是不能出现在引号之间的,因为编译mysql语句时,“#{}”会被"?"问号代替,结果就变成了'%?%',而问号占位符不能出现在引号之间。所以这里改成"${}"。 @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, Short gender, LocalDate begin , LocalDate end); //缺点:性能低、不安全、存在SQL注入问题 //解决方案:使用concat() @Select("select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and " + "entrydate between #{begin} and #{end} order by update_time desc ") public List<Emp> list(String name, Short gender, LocalDate begin , LocalDate end);
参数名说明
在早期springboot或者单独使用mybatis时,形参名需要添加注解@Param(),因为早期没有某个编译插件,编译之后别名会被替换掉,无法保存下来,那么对字段的封装就要求很高了。而使用@Param()可以解决这个问题。
5.3.5 XML映射文件
-
规范
- XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放在相同包下(同包同名)
- XML映射文件的namespace属性为Mapper接口全限定名(类名)一致
- XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致
-
第一条规范
- 先在resource目录下创建一个含多级目录且跟Mapper包名相同的包(创建含多级目录的包,域名之间不应该用点分隔,而应该用斜杠)
- 然后在创建的包中创建XML文件,直接在包中按照:文件->新建->文件->EmpMapper.xml创建即可。
-
第二条规范
- 在XML配置文件中都得有约束,我们先将约束代码复制粘贴到XML文件中(搜索mybatis中文网,在入门栏找到“探究已映射的SQL语句”)
<?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>
</mapper>
-
然后将mapper的namespace属性跟接口名(带包名)一致。
<?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 = "com.itheima.mapper.EmpMapper"> </mapper>
-
第三条规范
-
将sql语句写在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 = "com.itheima.mapper.EmpMapper"> <!--接口方法名就叫做list--> <!-- resultType:单条记录所封装的类型 ,我们这里把字段封装到了一个Emp类上。这里就是保持返回类型一致--> <select id = "list" resultType = "com.itheima.pojo.Emp"> select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and entrydate between #{begin} and #{end} order by update_time desc </select> </mapper>
//然后执行这个方法 public List<Emp> list(String name, Short gender, LocalDate begin , LocalDate end);
使用注解可以让代码看上去更简洁,但对于复杂的语句。Java注解不仅力不从心,还会让本就复杂的SQL语句变得混乱不堪。因此对于复杂的操作,最好用XML来映射语句。
-