全面学习MyBatis框架入门与提高(含代码)

这一篇文章我将和大家一起回顾整个MyBatis框架技术,由于内容较多,内容共分为三大部分。东西是当时我老师讲的,现在将知识点都整理下来,供大家一起讨论学习,也方便以后自己复习。如果有不对的地方欢迎大家指正。

1.MyBatis概念、配置连接、配置映射、…
2.获取自增主键值、使用多个参数、关联查询 …
3.占位符的使用、动态SQL …

<1>.MyBatis概念、配置连接、配置映射、…

1. 前课准备

  1. 创建数据库tedu_ums

    create database tedu_ums;
    
  2. 创建数据表t_user,该表中至少包含:idusernamepasswordagephoneemail,字段约束可自行设计,并保留SQL语句,下同

    create table t_user (
    	id int auto_increment,
    	username varchar(16) unique not null,
    	password varchar(16) not null,
    	age int,
    	phone varchar(20),
    	email varchar(50),
    	primary key(id)
    ) default charset=utf8;
    
  3. t_user表添加不少于10条数据

    insert into t_user (
    	username, password, age, phone, email
    ) values (
    	'root', '12r435r34', 21, '13800138001', '[email protected]'
    ), (
    	'admin', '12hghe34', 22, '13800138002', '[email protected]'
    ), (
    	'spring', '1234', 23, '13800138003', '[email protected]'
    ), (
    	'mybatis', '12cxv34', 24, '13800138004', '[email protected]'
    ), (
    	'html', '12876734', 25, '13800138005', '[email protected]'
    ), (
    	'filter', '12132134', 26, '13800138006', '[email protected]'
    ), (
    	'jdbc', '1221434334', 27, '13800138007', '[email protected]'
    ), (
    	'java', '12r54334', 28, '13800138008', '[email protected]'
    ), (
    	'mvc', '12ferdaf34', 29, '13800138009', '[email protected]'
    ), (
    	'servlet', '12fdsaf34', 30, '13800138010', '[email protected]'
    );
    
  4. 查询t_user表中所有数据

    select id, username, password, age, phone, email from t_user;
    
  5. 获取t_user表中数据的数量

    select count(id) from t_user;
    
  6. 获取t_user表指定username值为xx的数据

    select id, username, password, age, phone, email from t_user where username='mvc';
    
  7. 获取t_user表中年龄从高到低排列的前5条数据

    select id, username, password, age, phone, email from t_user order by age desc limit 0, 5;
    
  8. 删除t_user表中指定username值为xx的数据

    delete from t_user where username='mvc';
    
  9. t_user表中年龄大于xx的数据的密码修改为xx

    update t_user set password='P@ssw0rd' where age>26;
    
  10. 修改t_user表中指定id为xx的数据的电子邮箱是xx

    update t_user set email='[email protected]' where id=20;
    

2. MyBatis框架

2.1. 作用

解决持久层数据处理的问题,主要是基于JDBC技术的原生代码比较繁琐,没有经过任何优化,开发甚至执行效率低下!

使用MyBatis框架时,不必关心JDBC技术如何实现,只需要编写需要执行的操作的抽象方法,例如User findById(Integer id),然后,为这个方法映射所需执行的SQL语句即可。

2.2. 第1次使用MyBatis插入数据

前提:在数据库系统中已经存在tedu_ums数据库,且存在t_user表,结构可参考昨天的作业。

步骤1:创建项目

创建时,Artifact IdMYBATISGroup Idcn.tedu.mybatis

创建过程与前次课程相同。

步骤2:创建实体类

通常,每张数据表都有一个与之对应的实体类,在实体类中,有相同数量的属性,数据类型应该保持一致,属性名称与字段名应该一一对应(在Java中的属性名称应该采用驼峰命名法,而数据库领域中并不区分大小写),所有的属性都应该是私有的,且都存在公有的SET/GET方法,整个实体类应该是实现了Serializable接口的!

步骤3:添加依赖



	org.mybatis
	mybatis
	3.4.6




	org.mybatis
	mybatis-spring
	1.3.2




	org.springframework
	spring-jdbc
	4.3.9.RELEASE




	mysql
	mysql-connector-java
	8.0.13




	commons-dbcp
	commons-dbcp
	1.4




	junit
	junit
	4.9

注意:下载的新的依赖的jar可能是损坏的文件,如果保证代码正确的前提下,无法得到预期的运行效果,应该删除本地仓库中的jar包并重新更新!

步骤4:配置数据库连接

src\main\resources下创建db.properties文件,用于配置数据库连接的相关信息:

# data-source
url=jdbc:mysql://localhost:3306/tedu_ums?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
driver=com.mysql.cj.jdbc.Driver
username=root
password=root
initialSize=2
maxActive=10

然后,在src\main\resources下使用spring-dao.xml配置:






	
	
	
	
	
	
	

步骤5:编写方法

使用MyBatis时,无需自行编写JDBC相关代码,只需要创建Java接口文件,并将需要执行的数据操作的抽象方法添加在接口中即可!

通常,建议按照“增 > 查 > 删 > 改”的顺序开发相关功能。

目标:向数据表中插入新的用户数据。

则创建cn.tedu.mybatis.mapper.UserMapper接口,然后,添加“插入新的用户数据”的抽象方法:

Integer insert(User user);

使用MyBatis时,执行的增、删、改操作均返回Integer,表示受影响的行数。

步骤6:编写XML映射

使用MyBatis时,还需要与接口的抽象方法对应的SQL语句,该SQL语句是在XML文件中配置的!

从FTP下载somemapper.zip,得到所需的XML文件,并重命名为UserMapper.xml

通常,接口文件的数量与XML映射文件的数量是相同的,是一一对应的!

映射的XML文件应该存放到src\main\resources下,但是,项目中可能存在多个映射文件,为了便于管理,会在resources下创建mappers文件夹,然后把映射的XML文件放在这个文件夹中。

然后,配置该XML映射文件:




	
	
	
	
	
	
		INSERT INTO t_user (
			username, password,
			age, phone, email
		) VALUES (
			#{username}, #{password}, 
			#{age}, #{phone}, #{email}
		)
	


步骤7:完成MyBatis的配置



	
	




	
	
	
	

步骤8:执行单元测试

public class UserMapperTestCase {

	@Test
	public void insert() {
		AbstractApplicationContext ac
			= new ClassPathXmlApplicationContext(
					"spring-dao.xml");
		
		UserMapper userMapper
			= ac.getBean("userMapper", UserMapper.class);
		
		User user = new User();
		user.setUsername("mapper");
		user.setPassword("1234");
		user.setAge(31);
		user.setPhone("13900139001");
		user.setEmail("[email protected]");
		
		Integer rows = userMapper.insert(user);
		System.out.println("rows=" + rows);
		
		ac.close();
	}
	
}

2.3. 获取当前表中所有用户数据

分析需要执行的SQL语句:

select * from t_user

设计抽象方法:

List findAll();

在查询时,MyBatis会得到List集合类型的结果,如果存在匹配的数据,则全部封装在该List集合中,如果没有匹配的数据,则返回的List集合是长度为0的集合。

配置映射:


任何查询都必须指定resultType属性,当然,也可以替换为resultMap,关于resultMap,下周再讲……

2.4. 根据用户名查询用户数据

分析需要执行的SQL语句:

select * from t_user where username=?

设计抽象方法:

User findByUsername(String username);

查询时,如果返回值声明为某数据类型,却不是List集合,MyBatis会尝试从List集合中取出第1个元素,如果存在,则返回该元素,如果不存在(没有匹配的结果,List集合的长度为0),则返回null

配置映射:


2.5. 获取t_user表中数据的数量

分析需要执行的SQL语句:

select count(id) from t_user

设计抽象方法:

Integer getCount();

在查询时,查询结果与返回值的类型相匹配即可!所以,如果查询的是数据的数量,查询结果是数字,所设计的抽象方法的返回值就可以是Integer。

配置映射:


目前,所设计的抽象方法只允许存在最多1个参数!

2.6. 小结

  1. MyBatis使用简单,可以简化开发,开发者不必关注数据库编程的细节;

  2. 使用MyBatis编程主要做好:(1)设计SQL语句; (2)设计抽象方法; (3)配置映射;

  3. 所有的xx.propertiesspring-dao.xml中的配置,需要理解,需要掌握修改配置值,不需要记住;

  4. 关于抽象方法:如果是增删改操作,返回值固定设计为Integer,如果是查询操作,根据查询结果来决定,例如可能是ListUserInteger……;方法的名称应该尽量对应所执行的数据操作,而不应该是某个业务,例如插入数据的方法名可以是insert,或者addnew,但是不应该使用reg;目前,只允许使用最多1个参数,如果一定要使用多个,请封装为1个参数;

  5. 关于映射配置:根据所执行的操作选择 select id, username, password, age, phone, email, is_delete AS isDelete from t_user

    也就是:MyBatis的要求是“查询结果中的列名与返回值类型的属性名必须一致”,通过自定义别名就可以满足这个要求,并不需要通过来实现!

    通常,需要自定义时,主要用于解决多表数据关联查询的问题。

    例如:

    create table t_department (
    	id int auto_increment,
    	name varchar(20) not null,
    	primary key(id)
    );
    
    insert into t_department (name) values ('UI'), ('RD'), ('TEST');
    
    alter table t_user add column department int;
    

    通常实体类都是与数据表一一对应的,符合设计规范,但不适用于多表的关联查询,例如当需要“查询某个部门信息的同时需要获取该部门的所有员工的信息”,则没有任何数据类型可以匹配这样的信息,为了解决这样的问题,通常会定义VO类,即Value Object类,这种类型是专用于解决实体类不满足使用需求而存在的,类的设计结构与实体类非常相似,但是,属性的设计是完全根据使用需求来决定的,例如:

    public class DepartmentVO {
    	private Integer depId;
    	private String depName;
    	private List users;
    	// SET/GET方法,toString(),序列化接口
    }
    

    普通的查询无法得到以上结果,查询语句可能是:

    select
    	t_user.id, username, password, age, phone, email, is_delete,
    	t_department.id AS dep_id, name
    from 
    	t_user, t_department
    where
    	t_user.department=t_department.id
    	and t_department.id=?
    

    以上查询易于理解,通俗易懂,但是,不推荐使用,更推荐使用JOIN系列的查询语法:

    SELECT
    	t_user.id, username, password, age, phone, email, is_delete,
    
    	t_department.id AS dep_id, name
    FROM
    	t_user
    INNER JOIN
    	t_department
    ON
    	t_user.department=t_department.id
    WHERE
    	t_department.id=?
    

    这样的查询结果可能有好几行,

    需要有效的封装到1个对象中,就必须依靠来设计封装的规则:

    
    
    
    	
    	
    	
    	
    	
    	
    	
    	
    	
    		
    		
    		
    		
    		
    		
    		
    		
    	
    
    

    配置方式可参考:

    全面学习MyBatis框架入门与提高(含代码)_第2张图片

    最后,应用时,与普通的数据操作相同,先添加接口与抽象方法:

    public interface DepartmentMapper {
    
    	DepartmentVO findById(Integer id);
    
    }
    

    然后配置映射:

    
    

    最终,执行查询获取的结果例如:

    DepartmentVO [
    	depId=2, 
    	depName=RD, 
    	users=[
    		User [id=13, username=spring, password=1234, age=23, phone=13800138003, [email protected], isDelete=1, department=null], 
    		User [id=14, username=mybatis, password=12cxv34, age=24, phone=13800138004, [email protected], isDelete=0, department=null], 
    		User [id=17, username=jdbc, password=88888888, age=27, phone=13800138007, [email protected], isDelete=1, department=null], 
    		User [id=21, username=mapper, password=88888888, age=31, phone=13900139001, [email protected], isDelete=1, department=null], 
    		User [id=23, username=namespace, password=88888888, age=31, phone=13900139002, [email protected], isDelete=1, department=null]
    	]
    ]
    

    如果提示错误TooManyResultsException,则错误多半在于查询结果的列名与中普通的节点的column的配置有误!也有可能存在例如2列的名称都是id,却有多条数据的id值不同的问题!

    练习:存在学生表和班级表,学生表t_student中包括id, name, age, class_id,班级表t_class中包括id, name,最终,查询时,查某班级数据时将显示该班级所有学生的信息。涉及的类为Student、Clazz。

    练习步骤1:创建2张数据表,插入一定量的数据,创建对应的实体类,创建班级的VO类:

    create table t_class (
    	id int auto_increment,
    	name varchar(20),
    	primary key (id)
    );
    
    insert into t_class (name) values ('JSD1806'),('JSD1807'),('JSD1808');
    
    create table t_student (
    	id int auto_increment,
    	name varchar(20),
    	age int,
    	class_id int,
    	primary key (id)
    );
    
    insert into t_student (name,age,class_id) values ('Mike', 20, 1), ('Tom', 21, 2), ('Terry', 21, 3), ('Jerry', 22, 2), ('Lucy', 22, 1), ('Kitty', 22, 2), ('Lily', 21, 3), ('Lilei', 20, 3), ('HanMM', 23, 3), ('XiaoMing', 21, 2);
    

    练习步骤2:创建cn.tedu.mybatis.mapper.ClazzMapper接口,复制得到src\main\resources\ClazzMapper.xml映射文件,这2个文件都是空文件即可。

    练习步骤3:设计SQL语句:

    SELECT 
    	t_class.id AS cls_id, 
    	t_class.name AS cls_name,
    
    	t_student.id AS stu_id, 
    	t_student.name AS stu_name,
    	age, class_id
    FROM
    	t_class
    INNER JOIN
    	t_student
    ON
    	t_class.id=t_student.class_id
    WHERE
    	t_class.id=?;
    

    练习步骤4:抽象方法

    ClazzVO findById(Integer id);
    

    练习步骤5:配置 SELECT t_class.id AS cls_id, t_class.name AS cls_name, t_student.id AS stu_id, t_student.name AS stu_name, age, class_id FROM t_class INNER JOIN t_student ON t_class.id=t_student.class_id WHERE t_class.id=#{id};

    练习步骤6:配置

    
    	
    	
    	
    		
    		
    		
    		
    	
    
    

    课后练习:新添加考试成绩表t_score,包括字段id(int)、stu_id(int), subject(varchar), score(int),要求最终实现:根据学生id查询出该学生的所有成绩,例如:

    XX学生成绩单
    
    学号:xx		姓名:xx
    
    序号 | 科目 | 分数
    1      Java  80
    2      SQL   70
    

    <3>.占位符的使用、动态SQL …

    1. MyBatis中的占位符

    在MyBatis中,常见的占位符格式是#{参数},其中,也可能是参数对象中的属性,如果参数是Map类型,还可以是Map中的key。

    使用#{}的占位符可用于替换,例如:

    select * from t_user where username=?
    

    即可替换以上语句中的问号(?),在实际运行时,MyBatis会将以上SQL语句进行预编译,并后续使用#{}替换问号(?)。

    假设获取用户列表时,排序规则不确定,可能使用的抽象方法是:

    List findAllOrderedList(String orderBy);
    

    配置的映射可能是:

    
    

    调用时:

    mapper.findAllOrderedList("id asc");
    mapper.findAllOrderedList("id desc");
    

    以上代码的执行效果是失败的!需要将#{}修改为${},且在抽象方法中,这样的参数必须添加@Param注解,即:

    List findAllOrderedList(
    	@Param("orderBy") String orderBy);
    
    
    

    然后,在调用时,就可以根据参数的不同,实现不同的排序效果!

    使用${}格式的占位符并不具备预编译的效果!它是直接拼接形成的SQL语句,例如:"select * from t_user order by" + orderBy,如果一定使用${}格式的占位符来表示某个值,还需要考虑单引号类似的问题,例如:select * from t_user where username='${username}',由于只是拼接,所以,还存在SQL注入风险!

    小结

    使用#{}是预编译的(没有SQL注入风险,无需关注数据类型),使用${}不是预编译的;

    使用#{}只能替换某个值,使用${}可以替换SQL语句中的任何部分;

    关于SQL注入,不需要太过于紧张,预编译可以从根源上杜绝,或者,在执行SQL指令之前,判断参数中是否包含单引号也可以杜绝!

    通过使用${},可以使得SQL更加灵活,更加通用!但是,却不推荐太过于通用的SQL!因为,即使查询条件可以自由更改,但是,不同的查询条件对应不同的需求,所需的字段列表很有可能是不一样的,查询时,获取不必要的字段,就会造成不必要的资源浪费,例如,显示列表时,可能需要用户名、密码、年龄、手机、邮箱,但是,登录的查询就只需要用户名、密码即可,年龄、手机、邮箱这几项数据在登录时是不需要的,如果也查询出来,就是浪费资源!如果变量太多,又会导致不可控因素太多,容易出错!

    2. 动态SQL

    在MyBatis的映射文件中,配置SQL语句时,可以添加例如此类的标签,实现SQL语句的动态变化,即:参数不同,最终执行的SQL语句可能是不同的!

    在使用动态SQL时,最常用的就是这两种,是用于判断的,例如:

    select 
    	* 
    from 
    	t_user
    	
    
    where 
    	${where}
    
    	
    
    order by 
    	${orderBy}
    
    

    关于,主要用于循环处理SQL语句中的某个部分,例如:批量删除某些数据!它的SQL语句可能是:

    delete from t_user where id in (?,?,?)
    

    其中,in关键字右侧的括号中的内容是不确定的,应该是由用户操作时决定的!则需要动态的生成这个部分!

    针对这个问题,设计的抽象方法可能是:

    Integer deleteByIds(Integer[] ids);
    

    配置的映射为:

    
    	delete from 
    		t_user
    	where
    		id in (
    		
    			#{id}
    		
    		)
    
    

    以上配置的中,collection表示被遍历的集合对象,当抽象方法只有1个参数时,取值为listarray,取决于集合对象的数据类型,当抽象方法 有多个参数时,使用@Param注解中的名称,item表示遍历过程中的变量名,separator表示分隔符。

    以上配置还可以调整为:

    id in
    
    	#{id}
    
    

    即:open表示由处理的SQL语句的起始部分的字符串,而close表示结束部分的字符串。

你可能感兴趣的:(MyBatis框架,java,mybatis)