在MyBatis中查询数据、涉及多参数的数据访问操作…
2018-12-06 07:34:30来源:博客园 阅读 ()
1. 单元测试
在单元测试中,每个测试方法都需要执行相同的前置代码和后置代码,则可以自定义2个方法,分别在这2个方法中执行前置代码和后置代码,并为这2个方法添加@Before
和@After
注解,然后,在每个测试方法中,就不必再编写这些代码,最终,在执行测试方法之前,会自动调用添加了@Before
注解的方法,在执行测试方法之后,会自动调用添加了@After
注解的方法:
private AbstractApplicationContext ac;
private UserMapper userMapper;
@Before
public void doBefore() {
// 加载Spring配置文件,获取Spring容器
ac = new ClassPathXmlApplicationContext(
"spring.xml");
// 从Spring容器中获取对象
// bean-id与接口名一致,首字母为小写
userMapper
= ac.getBean("userMapper", UserMapper.class);
}
@After
public void doAfter() {
// 释放资源
ac.close();
}
@Test
public void deleteUserById() {
// 测试功能
Integer id = 3;
Integer rows = userMapper.deleteUserById(id);
System.out.println("rows=" + rows);
}
2. 在MyBatis中查询数据
使用MyBatis执行查询操作时,抽象方法的设计中,返回值应该根据查询需求来决定,例如查询用户列表时,可以使用List<User>
作为返回值类型,查询某个用户时,可以使用User
作为返回值类型,查询的是计数等操作时,可以使用Integer
作为返回值类型。
其实,无论执行什么样的查询,MyBatis的查询结果都是List集合,只不过,如果抽象方法声明的不是集合,而是具体的某个类型,例如User时,MyBatis会尝试从集合中获取第1个元素作为返回值!
查询是可能失败,即没有匹配的数据,在这种情况下,如果返回值是List集合,则返回的是空集合(集合对象是存在的,但是集合中没有任何元素),如果返回值是某个类型,则返回null。
在配置的XML映射中,每个查询对应的都是<select>
节点,该节点必须配置resultType
或resultMap
属性,用于表示查询结果的类型(即使返回值类型是Integer
也必须配置)!
如果返回结果是User对象,则resultType的值是User类的全名,如果返回结果是List,其resultType也是User类的全名,而不是List的全名!
关于resultMap后续再介绍。
目标:查询所有用户的数据
在接口中添加抽象方法:
List<User> findAll();
配置以上方法的映射:
<select id="findAll"
resultType="cn.tedu.mybatis.entity.User">
SELECT
id, username, password, age, phone, email
FROM t_user
</select>
目标:查询指定id的用户的数据
在接口中添加抽象方法:
User findUserById(Integer id);
配置以上方法的映射:
<select id="findUserById"
resultType="cn.tedu.mybatis.entity.User">
SELECT
id, username, password, age, phone, email
FROM t_user
WHERE id=#{id}
</select>
练习:根据用户名查询用户数据。
目标:获取当前用户表中用户的数量
在接口中添加抽象方法:
Integer getCount();
配置以上方法的映射:
<select id="getCount"
resultType="java.lang.Integer">
SELECT COUNT(id) FROM t_user
</select>
2. 涉及多参数的数据访问操作
在许多操作中,也许1个参数并不能满足需求,可能需要2个或更多个参数,例如修改某个用户的密码,设计的抽象方法可能是:
Integer changePassword(Integer id, String password);
然后,配置的映射应该是:
<update id="changePassword">
UPDATE t_user
SET password=#{password}
WHERE id=#{id}
</update>
在MyBatis中,其实只支持1个参数,并且,Java源文件编译为class字节码文件后,也会丢失参数名称,所以,需要为每一个参数添加@Param
注解:
Integer changePassword(
@Param("id") Integer arg0,
@Param("password") String arg1);
则MyBatis会把这些参数全部封装在一个Map
中,注解中的名称就是每个参数的Key,后续,在配置映射时,在#{}
中填入的也就是注解的名称,即各参数在Map中的Key:
在设计MyBatis中的抽象方法时,如果参数超过1个,则每个参数之前都必须添加@Param注解,在映射的SQL语句中,#{}中填入也是注解中使用的名称!
3. 插入数据时获取数据自增长的id
在配置映射时,为<insert>
节点添加2个属性:useGeneratedKeys="true"
表示需要获取自动生成的键,通常就是数据表中的主键字段,即id字段,然后配置keyProperty="id"
,表示获取到的键的值(即自增长的id值)将要封装到哪个属性中(即实体类中的属性名):
经过以上配置后,当成功插入数据时,就会获取到该数据的自增长的id值,并且,会将值封装到参数对象中,即:调用Integer insert(User user)
方法时,假设使用user1
作为调用时的参数,当方法执行结果后,参数user1
中就已经包含了id值!
@Test
public void insert() {
// 测试功能
User user = new User();
user.setUsername("java");
user.setPassword("1234");
System.out.println("执行前:" + user);
Integer rows = userMapper.insert(user);
System.out.println("rows=" + rows);
System.out.println("执行后:" + user);
}
执行结果例如:
4. 关联表查询操作
问题描述
存在班级和学生数据,要求显示某个班级信息时,同时显示出该班级的所有学生。
准备工作
创建班级(1)表:
CREATE TABLE t_class (
id INT AUTO_INCREMENT,
name VARCHAR(20),
PRIMARY KEY(id)
) DEFAULT CHARSET=UTF8;
创建学生(N)表:
CREATE TABLE t_student (
id INT AUTO_INCREMENT,
name VARCHAR(20),
class_id INT,
PRIMARY KEY(id)
) DEFAULT CHARSET=UTF8;
当数据表之间存在1对多关系时,在“多”的表中需要存储“1”的表的唯一标识,即存储“1”的表的id。
为了保证后续的数据查询,还应该添加一部分的测试数据:
INSERT INTO t_class (name) VALUES
("JSD1806"), ("JSD1807"), ("JSD1808");
INSERT INTO t_student (name, class_id) VALUES
("Jack", 1), ("Rose", 1),
("LiLei", 2), ("HanMM", 2),
("Lucy", 3), ("LiLi", 3),
("Bob", 1), ("LiMing", 1),
("Kitty", 2), ("Tom", 1);
基于以上数据表,如果要获取班级信息的同时,还获取该班级的所有学生的列表,则执行的SQL查询应该是:
SELECT
c.id AS class_id,
c.name AS class_name,
s.id AS student_id,
s.name AS student_name
FROM t_class AS c
INNER JOIN t_student AS s
ON c.id=s.class_id
WHERE c.name="JSD1806";
基于每张数据表都应该有1个与之对应的实体类的原则,则在项目中应该有:
public class Clazz {
private Integer id;
private String name;
}
public class Student {
private Integer id;
private String name;
private Integer classId;
}
即使有了以上2个类,却都无法满足查询结果的需求,即尝试在接口中声明抽象方法时:
??? getClassInfo(String className);
无法确定返回值类型!
针对这种情况,通常会在项目中创建VO类,即Value Object类!例如:
public class ClazzVO {
private Integer classId;
private Integer className;
private List<Student> students;
}
通常,实体类与数据表是对应的,而VO类是与实际使用需求对应的!
当设计好了VO类,则查询的抽象方法可以是:
ClazzVO getClassInfo(String className);
可以发现,即使使用了VO类,查询的字段与ClazzVO
类中的属性名称等都无法直接对应,则,在映射文件中,需要使用到<resultMap>
!
最后的执行结果为:
ClazzVO [
classId=1,
className=JSD1806,
students=[
Student [id=1, name=Jack, classId=null],
Student [id=2, name=Rose, classId=null],
Student [id=7, name=LiMing, classId=null],
Student [id=8, name=Bob, classId=null],
Student [id=10, name=Tom, classId=null]
]
]
小结
如果某次查询涉及多张表,存在关联查询,通常是没有匹配的实体类可以直接使用的,在这种情况下,就需要自定义VO类。
VO类与实体类的代码表现基本相似,只是定位不同,实体类是与数据表对应的,而VO类是为了满足编码需求,更方便的获取查询结果而存在的!
VO类的属性的设计原则完全取决于所需要执行的查询的结果。
产生了关联查询后,可以直接用VO类作为resultType
,但是,如果查询结果中存在数据之间的1对多等关系,则需要配置<resultMap>
。
5. 动态SQL
在MyBatis中配置映射时,允许使用例如<if>
此类的标签,使得每次执行的SQL语句可以产生动态调整,则称之为动态SQL。
目标:实现根据id修改用户信息,可修改的字段有:密码、年龄、手机号码、电子邮件,如果执行的参数中,某项数据为null,则不修改原有值,例如修改时,参数中没有年龄值,则不修改原有的年龄,其它字段也是相同的处理方式。
首先,在接口中声明抽象方法:
Integer changeInfo(
@Param("id") Integer id,
@Param("password") String password,
@Param("age") Integer age,
@Param("phone") String phone,
@Param("email") String email);
然后,配置以上方法的映射:
UPDATE t_user
SET
password=#{password},
age=#{age},
phone=#{phone},
email=#{email}
WHERE id=#{id}
以上配置的执行效果会是:如果没有提供某个值,将会把对应的字段的值设置为null,而并非不修改原有值。
此类问题可以通过动态SQL的<if>
标签来解决:
UPDATE t_user
SET
password=#{password},
<if test="age != null">
age=#{age},
</if>
phone=#{phone},
<if test="email != null">
email=#{email}
</if>
WHERE id=#{id}
执行以上代码时,如果提供了有效的age值(非null),则SQL语句为:
UPDATE t_user SET password=?, age=?, phone=?, email=? WHERE id=?
但是,如果没有提供age值,则SQL语句为:
UPDATE t_user SET password=?, phone=?, email=? WHERE id=?
注意:在编写动态SQL时,参数直接写名字即可,例如test="age != null"中的age就是参数的名称,不需要使用#{}这类的语法!
目标:一次删除多条数据,这些数据的id是作为参数体现的,但是,是没有规律的。
在接口中声明抽象方法:
Integer deleteUserByIds(List<Integer> ids);
以上方法的设计,参数可以是List<Integer>
,也可以是Integer[]
。
然后,配置映射:
<!-- collection:使用哪个集合,取值使用list或array -->
<!-- item:每次遍历到的元素的名称 -->
<!-- separator:IN内部的各个值之间使用的分隔符 -->
<!-- open:遍历后的结果的起始字符 -->
<!-- close:遍历后的结果的结束字符 -->
<delete id="deleteUserByIds">
DELETE FROM t_user
WHERE id IN
<foreach collection="list"
item="id" separator=","
open="(" close=")">
#{id}
</foreach>
</delete>
注意:在中的collection属性,当抽象方法只有1个参数时,取值为list或array,根据参数类型决定,当抽象方法的参数超过1个时,该属性的值为参数的名称(参数的注解中使用的名称)!
关于动态SQL,主要掌握<if>
和<foreach>
的使用!
【附】关于配置MyBatis映射没有代码提示的解决方案**
先连接达内公司内网,下载:http://schema.tedu.cn/proxy/dtd/ibatis-3-mapper.dtd
下载的文件存储到任意位置均可。
在Eclipse中打开设置,左侧选择XML > XML Catelog,并在右侧点击Add按钮:
然后,在左侧选择第1项,右侧选择到刚才下载的文件,然后在Key这一栏输入-//ibatis.apache.org//DTD Mapper 3.0//EN
(在映射文件顶部Public
字样右侧的字符串):
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:序列化和反序列化
下一篇:关于java中反射的小结
- MyBatis中的$和#,用不好,准备走人! 2020-06-11
- Flink 如何分流数据 2020-06-11
- 数据源管理 | Kafka集群环境搭建,消息存储机制详解 2020-06-11
- SpringBoot 2.3 整合最新版 ShardingJdbc + Druid + MyBatis 2020-06-11
- 大公司都在做的大数据平台,为你精选这一份书单 2020-06-09
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash