MyBatis框架基础知识(02)

2021-08-23 15:16:02 浏览数 (1)

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文件对应的接口的全名(包名与类名),例如:

代码语言: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">

<mapper namespace="cn.tedu.spring.UserMapper">
</mapper>

然后,在<mapper>节点的子级,根据需要执行的SQL语句的种类,在<insert><delete><update><select>这4个节点类型中选取所需要使用的节点,这些节点都需要配置id属性,取值就是对应的抽象方法的名称,然后,将SQL语句配置在节点的子级,例如:

代码语言: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">

<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配置以上读取到的属性:

代码语言:javascript复制
@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接口中添加抽象方法:

代码语言:javascript复制
Integer deleteById(Integer id);

然后,在SomeMapper<mapper>节点的子级补充配置:

代码语言:javascript复制
<delete id="deleteById">
    DELETE FROM t_user WHERE id=#{id}
</delete>

完成后,在src/test/javacn.tedu.spring包下创建新的测试类UserMapperTests,编写并执行单元测试:

代码语言:javascript复制
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接口中添加抽象方法:

代码语言:javascript复制
Integer updatePassword(String password);

然后,在SomeMapper.xml中添加配置:

代码语言:javascript复制
<update id="updatePassword">
    UPDATE t_user SET password=#{password}
</update>

完成后,在UserMapperTests中添加测试:

代码语言:javascript复制
@Test
public void updatePassword() {
    String password = "8888";
    Integer rows = userMapper.updatePassword(password);
    System.out.println("rows="   rows);
}

3. 使用MyBatis实现查询

在使用MyBatis实现查询时,在设计抽象方法时,应该使用期望的类型作为抽象方法的返回值类型。

假设需要实现:统计当前数据表中用户的数量。

则可以将抽象方法设计为:

代码语言:javascript复制
Integer count();

在配置映射时,应该使用<select>节点,该节点必须配置resultTyperesultMap中的某1个属性(必须二选一):

代码语言:javascript复制
<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集合,则抽象方法可以设计为:

代码语言:javascript复制
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),则创建该数据表:

代码语言:javascript复制
CREATE TABLE t_group (
	id int AUTO_INCREMENT,
    name varchar(15) NOT NULL UNIQUE,
    PRIMARY KEY (id)
) DEFAULT CHARSET=utf8;

接下来,应该在项目中开发“用户组”数据表的管理功能,推荐创建新的类、接口来实现相关功能,首先,应该创建Group实体类,用于对应数据表:

代码语言:javascript复制
public class Group {
    private Integer id;
    private String name;
    
    // Getters & Setters
    // toString()
}

然后,在cn.tedu.spring包(@MapperScan注解配置的包)中创建GroupMapper接口,用于添加抽象方法,并在src/main/resourcesmappers文件夹下,粘贴得到GroupMapper.xml文件,用于配置SQL语句!

并完成以下功能:

  • 插入新的用户组数据;
  • 显示用户组列表;
  • 根据id删除某个用户组。

用户数据与用户组数据应该是有关联,即:某个用户归属于某个用户组,且每个用户组中可以有若干个用户。为了表现这样的关系,应该在用户数据表中添加新的字段,表示用户归属的组的id,所以,修改t_user表,添加group_id字段,需要执行的SQL语句大致是:

代码语言:javascript复制
ALTER TABLE t_user ADD COLUMN group_id int;

默认情况下,现有的每个用户的group_id字段的值都会是NULL值,应该为现有的每个用户数据随机的分配一些组的id,以使得测试数据是完整有效的,例如:

代码语言:javascript复制
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类中也添加新的属性:

代码语言:javascript复制
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

0 人点赞