1. 使用XML文件配置各抽象方法对应的SQL语句
使用@Insert
或相关注解配置SQL语句时,SQL语句与抽象方法的对应关系非常直观,但是,却不便于阅读、管理各SQL语句!因为在源代码中,SQL语句的表现就是一个字符串,在实际开发过程中,经常会使用到一些较长的SQL语句,如果使用1个字符串表示较长的SQL语句,在源代码就存在必须换行显示,又存在字符串拼接的问题!所以,非常不推荐使用@Insert
或相关注解来配置SQL语句!
在项目的src/main/resources中创建mappers文件夹。
从http://doc.canglaoshi.org/config/Mapper.xml.zip
下载文件,将下载得到的文件解压,得到SomeMapper.xml文件,这个文件就是专门用于配置SQL语句的文件!
将SomeMapper.xml复制到项目的mappers文件夹中!
在这个专门用于配置SQL语句的XML文件中,存在以下代码:
代码语言:javascript复制<?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">
这几行代码是不允许修改的,也是必须存在的!
在这个XML文件中,根节点必须是<mapper>
,在根节点中,必须配置namespace
属性,该属性值是这个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="cn.tedu.spring.UserMapper">
</mapper>
然后,在<mapper>
节点的子级,根据需要执行的SQL语句的种类,在<insert>
、<delete>
、<update>
、<select>
这4个节点类型中选取所需要使用的节点,这些节点都需要配置id
属性,取值就是对应的抽象方法的名称,然后,将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 namespace="cn.tedu.spring.UserMapper">
<insert id="aaa">
INSERT INTO t_user (
username, password, age, phone, email
) VALUES (
#{username}, #{password}, #{age}, #{phone}, #{email}
)
</insert>
</mapper>
【概念更新】在接口中设计抽象方法时,抽象方法的名称可以自定义,但是,不允许使用重载!
由于目前MyBatis框架还不知道这个XML文件的存在,所以,需要通过配置使框架能够使用该XML文件!应该先在jdbc.properties中添加配置:
代码语言:javascript复制mybatis.mapper-locations=classpath:mappers/*.xml
其实,此时,这个配置文件还叫jdbc.properties就已经有点不太合适了,因为其中还有其它的配置,但是,这个演示案例就暂时不修改文件名了。
然后,在SpringConfig中读取以上新增的配置:
代码语言:javascript复制@Value("${mybatis.mapper-locations}")
private Resource[] mapperLocations;
最后,在配置Spring框架获取SqlSessionFactoryBean
的对象时,为SqlSessionFactoryBean
配置以上读取到的属性:
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(mapperLocations);
return bean;
}
2. 使用MyBatis实现删除与修改
预先准备测试数据:
代码语言:javascript复制insert into t_user (username, password, age, phone, email) values
('root', '1234', 21, '13800138001', 'root@QQ.com'),
('spring', '4352', 28, '13800138009', 'spring@QQ.com'),
('random', '5635', 27, '13800138002', 'random@QQ.com'),
('mybatis', 'adef', 24, '13800138003', 'mybatis@QQ.com'),
('date', 'recv', 25, '13800138004', 'date@QQ.com'),
('calendar', '8454', 30, '13800138011', 'calendar@QQ.com'),
('string', 'xgvr', 23, '13800138005', 'string@QQ.com'),
('integer', 'df34', 25, '13800138006', 'integer@QQ.com'),
('servlet', 'hjfd', 26, '13800138007', 'servlet@QQ.com'),
('filter', '6xfd', 29, '13800138008', 'filter@QQ.com'),
('interceptor', '76fg', 22, '13800138010', 'interceptor@QQ.com');
只要是使用MyBatis框架开发持久层功能,需要开发的内容始终是:编写抽象方法,配置SQL语句。
假设需要实现:根据id删除用户数据。
需要执行的SQL语句大致是:
代码语言:javascript复制delete from t_user where id=?
则在UserMapper
接口中添加抽象方法:
Integer deleteById(Integer id);
然后,在SomeMapper的<mapper>
节点的子级补充配置:
<delete id="deleteById">
DELETE FROM t_user WHERE id=#{id}
</delete>
完成后,在src/test/java的cn.tedu.spring
包下创建新的测试类UserMapperTests
,编写并执行单元测试:
public class UserMapperTests {
@Test
public void deleteById() {
Integer id = 2;
Integer rows = userMapper.deleteById(id);
System.out.println("rows=" rows);
}
// =========================================
public AnnotationConfigApplicationContext ac;
public UserMapper userMapper;
@Before
public void doBefore() {
ac = new AnnotationConfigApplicationContext(SpringConfig.class);
userMapper = ac.getBean("userMapper", UserMapper.class);
}
@After
public void doAfter() {
ac.close();
}
}
注意:如果单元测试环境是JUnit 5,需要将@Before
和@After
换成@BeforeEach
和@AfterEach
。
假设需要实现 :将所有用户的密码都改为某个值。
需要执行的SQL语句大致是:
代码语言:javascript复制update t_user set password=?
则在UserMapper
接口中添加抽象方法:
Integer updatePassword(String password);
然后,在SomeMapper.xml中添加配置:
代码语言:javascript复制<update id="updatePassword">
UPDATE t_user SET password=#{password}
</update>
完成后,在UserMapperTests
中添加测试:
@Test
public void updatePassword() {
String password = "8888";
Integer rows = userMapper.updatePassword(password);
System.out.println("rows=" rows);
}
3. 使用MyBatis实现查询
在使用MyBatis实现查询时,在设计抽象方法时,应该使用期望的类型作为抽象方法的返回值类型。
假设需要实现:统计当前数据表中用户的数量。
则可以将抽象方法设计为:
代码语言:javascript复制Integer count();
在配置映射时,应该使用<select>
节点,该节点必须配置resultType
或resultMap
中的某1个属性(必须二选一):
<select id="count" resultType="java.lang.Integer">
SELECT COUNT(*) FROM t_user
</select>
完成后,测试:
代码语言:javascript复制@Test
public void count() {
Integer count = userMapper.count();
System.out.println("count=" count);
}
假设需要实现:根据id查询某用户的详情。
则抽象方法设计为:
代码语言:javascript复制User findById(Integer id);
配置映射:
代码语言:javascript复制<select id="findById" resultType="cn.tedu.spring.User">
SELECT * FROM t_user WHERE id=#{id}
</select>
单元测试:
代码语言:javascript复制@Test
public void findById() {
Integer id = 8;
User user = userMapper.findById(id);
System.out.println(user);
}
假设需要实现:查询当前数据表中所有用户的详情。
由于本次查询时,可能返回多个用户的数据,必须将返回值类型声明为“可以表示若干个用户信息”的数据类型,可以使用数组,或List
集合,则抽象方法可以设计为:
List<User> findAll();
然后,配置映射,本次抽象方法的返回值类型是List<User>
类型,在配置resultType
属性时,不需要告诉框架“这次返回List
集合”,因为,框架能够根据抽象方法的返回值创建出返回值对象,只需要告诉框架“集合中的元素是什么类型的”,以使得“框架能够将查询结果封装到一个个的对象中”。
所以,配置映射的代码为:
代码语言:javascript复制<select id ="findAll" resultType="cn.tedu.spring.User">
SELECT * FROM t_user ORDER BY id LIMIT 0, 100
</select>
完成后,单元测试:
代码语言:javascript复制@Test
public void findAll() {
List<User> users = userMapper.findAll();
System.out.println("count=" users.size());
for (User user : users) {
System.out.println(user);
}
}
* 后续准备工作
在tedu_ums
数据库中,新增数据表t_group
用于存储“用户组”,该数据表暂定只需要2个字段:id
、组名称(name
),则创建该数据表:
CREATE TABLE t_group (
id int AUTO_INCREMENT,
name varchar(15) NOT NULL UNIQUE,
PRIMARY KEY (id)
) DEFAULT CHARSET=utf8;
接下来,应该在项目中开发“用户组”数据表的管理功能,推荐创建新的类、接口来实现相关功能,首先,应该创建Group
实体类,用于对应数据表:
public class Group {
private Integer id;
private String name;
// Getters & Setters
// toString()
}
然后,在cn.tedu.spring
包(@MapperScan
注解配置的包)中创建GroupMapper
接口,用于添加抽象方法,并在src/main/resources的mappers文件夹下,粘贴得到GroupMapper.xml文件,用于配置SQL语句!
并完成以下功能:
- 插入新的用户组数据;
- 显示用户组列表;
- 根据id删除某个用户组。
用户数据与用户组数据应该是有关联,即:某个用户归属于某个用户组,且每个用户组中可以有若干个用户。为了表现这样的关系,应该在用户数据表中添加新的字段,表示用户归属的组的id,所以,修改t_user
表,添加group_id
字段,需要执行的SQL语句大致是:
ALTER TABLE t_user ADD COLUMN group_id int;
默认情况下,现有的每个用户的group_id
字段的值都会是NULL
值,应该为现有的每个用户数据随机的分配一些组的id,以使得测试数据是完整有效的,例如:
update t_user set group_id=1 where id IN (14,20,22,23);
update t_user set group_id=2 where id IN (21,15,16,24);
update t_user set group_id=3 where id IN (17,18,19);
由于修改了用户数据表的结构,还应该在User
类中也添加新的属性:
private Integer groupId; // 对应t_user表中的group_id
// Getters & Setters
// 重新生成toString()
提示:当添加了新的属性后,也无法直接通过原有的查询获取到对应的“组id”的值。
后续,将学习关联查询的处理方法,在2张表的情况下,可能存在的关联查询需求有:
根据id查询某个用户详情时,显示该用户归属的组的名称!需要执行的SQL语句大致是:
代码语言:javascript复制select
t_user.*, t_group.name
from t_user left join t_group on t_user.group_id=t_group.id where t_user.id=10;
根据id查询某个用户组的详情时,显示该组的所有用户的信息!需要执行的SQL语句大致是:
代码语言:javascript复制select * from t_group left join t_user
on t_group.id=t_user.group_id where t_group.id=3;
练习一:
- 实现“删除所有用户数据”;
练习二,写出以下需求对应的SQL语句:
- 根据id修改某用户的电子邮箱;
update t_user set email=? where id=?
- 一次性删除多条用户数据;
delete from t_user where id=? or id=? or id=?
delete from t_user where id in (?,?,?)
- 统计当前数据表中用户的数量;
select count(*) from t_user
- 根据id查询某用户的详情;
select * from t_user where id=?
- 根据用户名查询某用户的详情;
select * from t_user where username=?
- 查询当前数据表中所有用户的详情;
select * from t_user order by id limit 0, 100
- 假设当前数据表中每个用户的年龄都不同,找出年龄最大的那1个用户的详情。
select * from t_user where age=(select max(age) from t_user)
select * from t_user order by age desc limit 0, 1