- 1. 将id=?的数据的电子邮箱改为?
- 2. 动态SQL--foreach
- 3. 动态SQL--if
- 4. 关于#{}和${}占位符
- 5. 使用自定义别名解决名称不匹配的查询问题
- 6. 使用解决查询时名称不匹配的问题
- 7. 简单的关联表查询
- 8. 练习
在UserMapper接口中添加抽象方法:
Integer updateEmailById(
@Param("id") Integer id,
@Param("email") String email
);
如果抽象方法的参数的数量超过1个,应该在每个参数之前添加@Param注解,并在注解中配置参数的名称,后续在配置SQL语句时,使用的#{}占位符中也需要写注解中配置的名称!
在SomeMapper.xml中添加配置:
UPDATE t_user SET email=#{email} WHERe id=#{id}
在配置SQL时,如果抽象方法的参数只有1个,则在 #{} 占位符中写任意名称均可,因为框架会找唯一的那个参数!如果抽象方法的参数有多个(超过1个),由于.java文件在编译成.class文件时会丢失局部变量(参数也是一种局部变量)名称,所以,在#{}占位符中写参数名称是没有意义的,框架在执行时无法区分,可以使用#{arg0}表示第1个参数,使用#{arg1}表示第2个参数,以此类推,或者,使用#{param1}表示第1个参数,使用#{param2}表示第2个参数,以此类推……这样的做法虽然能解决问题,但是,SQL语句的代码的可读性就非常差,当抽象方法的参数都添加了@Param注解时,在SQL语句中就可以使用注解中配置的名称,以增加代码的可读性!
完成后,可以在Tests中编写单元测试:
@Test
public void updateEmailById() {
Integer id = 1;
String email = "u1@163.com";
Integer rows = userMapper.updateEmailById(id, email);
System.out.println("rows=" + rows);
}
2. 动态SQL–foreach
动态SQL:在配置SQL语句时,可以添加例如
例如存在“一次性删除若干条数据”的需求,到底要删除多少条数据,这些数据的id分别是多少,对于开发人员来说是未知的!则SQL语句就是无法确定的!就需要使用动态SQL中的foreach的做法!即要求用户提供若干个id的集合,然后遍历该集合,形成SQL语句中where id in ()括号中的若干个问号!
首先,还是应该在UserMapper接口中添加抽象方法:
Integer deleteByIds(Listids);
DELETE FROM t_user WHERe id IN #{id}
参数需要表示若干个id值,可以使用List集合,也可以使用数组,或者声明为可变参数(在处理过程中,等同于数组)。
然后,在SomeMapper.xml中添加配置:
在配置
-
collection:被遍历的参数对象。当抽象方法的参数只有1个,没有添加@Param注解时,当对象是List集合类型时,取值为list,当对象是数组类型时,取值为array;当抽象方法的参数有多个,并且添加了@Param注解时,该属性的值就是注解中定义的名称;
-
item:在遍历过程中,被遍历到的数据的名称,是自定义的,等同于for (Integer id : ids)中的id,并且,在子级的文本中使用#{}占位符时,使用的名称就是该item属性的值;
-
separator:分隔符,生成遍历结果时,每个数据之间的分隔符,即若干个id值之间的逗号;
-
open与close:遍历生成的SQL语句部分的最左侧和最右侧的字符串。
在配置SQL时,可以使用
注意:在动态SQL中,虽然有
注意:凡是能在Java程序中解决的问题,就尽量不要通过动态SQL来解决!
4. 关于#{}和${}占位符在MyBatis中配置SQL语句时,参数可以使用#{}格式的占位符,也可以使用${}格式的占位符!
使用#{}格式的占位符,是用于对值进行占位的,并不能表示SQL语句结构中的某个部分,也不可以表示某个表达式!而${}格式的占位符可以表示任何内容!
使用#{}格式的占位符,是采取预编码的做法的,即先无视值的大小,将SQL执行编译,编译通过后,再将值代进去来执行SQL语句!使用${}格式的占位符,是先把占位符的值拼接到SQL语句,再进行编译,编译通过后再运行的!
可以简单的认为:以前学JDBC使用PreparedStatement时,可以写问号?的位置,都可以使用#{}格式的占位符,反之,不可以写问号?的位置,就不能使用#{}格式的占位符,而需要使用${}格式的占位符!
由于使用${}格式的占位符不是预编译的,所以,还存在SQL注入的风险!
简单的来说,优先使用#{}格式的占位符,如果不正确,换成${}格式的占位符!
5. 使用自定义别名解决名称不匹配的查询问题先在t_user数据表中添加名为is_delete的字段,用于表示“某用户的数据是否标记为删除”,默认为0,表示“未删除,正常”,也可以设置为1表示“已删除”。
添加字段的SQL语句:
ALTER TABLE t_user ADD COLUMN is_delete INT; UPDATE t_user SET is_delete=0;
然后,User类应该是与t_user表相对应的,所以,还应该在User类中补充对应的属性:
private Integer isDelete; // SET/GET
添加之后,再次查询用户数据,会发现查询结果中isDelete的值是null!
因为MyBatis在处理查询时,默认情况下,要求查询结果的列名与封装结果的属性名(其实是SET方法的名称)保持一致!
既然目前名称并不一致,而属性名(SET方法名)是不可能随便调整的,就只能修改查询结果的列名,通过查询时指定别名就可以实现这样的效果:
6. 使用解决查询时名称不匹配的问题区分3个名词:
字段名(Field):创建数据表时指定的各字段的名称;
列名(Column):查询结果的表格中每项数据上方的名称,默认情况下,列名就是字段名,当然,也可以在查询时指定别名,则列名就是别名;
属性名(Property):类中定义的全局变量的名称;
如果查询结果的列名与封装结果的属性名不一致,且不指定别名,还可以通过配置
配置
配置代码如下:
配置完成后,在
类似以上这种单表的数据查询中,如果查询结果的列名与类中的属性名是完全相同的,其实可以不必配置,所以,以上
另外,除了使用
从查询结果看来,使用
创建t_department部门信息表:
CREATE TABLE t_department ( id INT AUTO_INCREMENT COMMENT '部门id', name VARCHAR(20) NOT NULL COMMENT'部门名称', PRIMARY KEY (id) ) DEFAULT CHARSET=utf8;
然后,还应该在项目中创建Department类:
public class Department {
private Integer id;
private String name;
// SET/GET/toString
}
如果要使得用户数据与以上部门的数据产生关联,还应该先在用户数据表中添加“用户所归属的部门”字段:
ALTER TABLE t_user ADD COLUMN department_id INT;
修改了用户数据表,则应该在User类中也补充添加相应的字段:
private Integer departmentId;
然后,在2张中调整一下测试数据:
INSERT INTO t_department (name) VALUES ('软件研发部'), ('人力资源部'), ('财务部');
UPDATE t_user SET department_id=1 WHERe id IN (1,8,15);
UPDATE t_user SET department_id=2 WHERe id IN (4,11,14);
UPDATE t_user SET department_id=3 WHERe id IN (9,10,13);
假设存在需求:查询某个用户的信息,并显示该用户归属的部门的名称。
需要执行的SQL语句大致大致是:
select t_user.id, username, password, age, phone, email, is_delete, department_id, name from t_user left join t_department on t_user.department_id=t_department.id where t_user.id=?
与数据表相对应的是实体类(Entity),当涉及关联表查询时,必然没有任何一个实体类可以封装查询结果,则需要自定义新的**VO类(Value Object)**来封装查询结果!这种类与实体类只是定位不同,它是与查询结果对应的,或者是根据查询结果来定制的,编码方式几乎一致!
所以,需要先创建VO类,表示此次的查询结果:
public class UserVO {
private Integer id;
private String username;
private String password;
private Integer age;
private String phone;
private String email;
private Integer isDelete;
private Integer departmentId;
private String departmentName;
}
在UserMapper接口中定义抽象方法:
UserVO findVOById(Integer id);
然后SomeMapper.xml中配置以上方法对应的SQL语句:
8. 练习
创建DepartmentMapper接口,用于定义管理部门数据的抽象方法;
将SomeMapper.xml重命名为UserMapper.xml;
将UserMapper.xml复制得到DepartmentMapper.xml,并清空其中的配置,该DepartmentMapper.xml将用于配置管理部门数据的SQL语句;
通过MyBatis框架实现以下功能:
-
增加新的部门;
-
修改部门的名称;
-
根据某个id删除部门;
-
批量删除部门;
-
查询部门列表;
-
根据某个id查询部门。



