mybatis(3)
一、动态sql
二、高级查询(一对一查询、一对多查询、多对多查询)
三、延迟加载
一、动态sql
1、if
if标签:判断语句
test属性:用来编写表达式,支持ognl.
案例:查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用户。
(1)定义接口
在UserMapper接口中定义方法:
/**
* 通过用户名进行模糊查询,如果没有用户名,查询所有男性用户
* @param userName
* @return
*/
List<User> queryUsersLikeUserName(@Param("userName")String userName);
(2)编写Mapper.xml
在UserMapper映射文件中,定义接口方法对应的Statement
<select id="queryUsersLikeUserName" resultType="User">
select * from tb_user where sex = 1
<!--
if标签:用来进行判断
test属性:编写ognl表达式
-->
<if test="userName!=null and userName.trim()!=''">
and user_name like '%' #{userName} '%'
</if>
</select>
(3)测试
当用户名为zhang时
@Test
public void testQueryUsersLikeUserName(){
List<User> list = userMapper.queryUsersLikeUserName("zhang");
for (User user : list) {
System.out.println(user);
}
}
2、choose when otherwise
choose标签:条件选择
when子标签:编写条件,不管有多少个when条件,一旦其中一个条件成立,后面的when条件都不执行。(相当于 if...else if...else)
test属性:编写ognl表达式
otherwise子标签: 当所有条件都不满足时,才会执行该条件。
案例:根据用户名或者年龄查询所有男性用户,如果输入了用户名则按照用户名模糊查找,否则就按照年龄查找,两个条件只能成立一个,如果都不输入就查找用户名为“zhangsan”的用户。
(1)定义接口
在UserMapper接口中定义方法:
/**
* 根据用户名或者年龄查找男性用户,如果有用户名就按用户名查找,否则按年龄查找,如果都没有,就查询名字是zhangsan的男性用户
* @param userName
* @param age
* @return
*/
List<User> queryUserListByUserNameOrAge(@Param("userName")String userName,@Param("age")Integer age);
(2)编写Mapper.xml
在UserMapper映射文件中,定义接口方法对应的Statement
<select id="queryUserListByUserNameOrAge" resultType="User">
select * from tb_user where sex = 1
<choose>
<when test="userName!=null and userName.trim()!=''">
and user_name like '%' #{userName} '%'
</when>
<when test="age!=null">
and age = #{age}
</when>
<otherwise>
and user_name = "zhangsan"
</otherwise>
</choose>
</select>
(3)测试
当既有用户名又有年龄时
@Test
public void testQueryUserListByUserNameOrAge(){
List<User> list = userMapper.queryUserListByUserNameOrAge("zhang",20);
for (User user : list) {
System.out.println(user);
}
}
3、Where
where标签: 可以自动将动态sql中多出来的一个and或者or去除。
解释:比如输入用户名和年龄进行查询,若其中一个为空,则自动去除这个条件(and)。
案例:查询所有用户,如果输入了用户名按照用户名进行模糊查询,如果输入年龄,按年龄进行查询,如果两者都输入,两个条件都要成立。
(1)定义接口
在UserMapper接口中定义方法:
/**
* 根据用户名和年龄查找用户。
* @param userName
* @param age
* @return
*/
List<User> queryUserListByUserNameAndAge(@Param("userName")String userName,@Param("age")Integer age);
(2)编写Mapper.xml
在UserMapper映射文件中,定义接口方法对应的Statement(若其中一个为空,则自动去除这个条件(and))
<select id="queryUserListByUserNameAndAge" resultType="User">
select * from tb_user
<where>
<if test="userName!=null and userName.trim()!=''">
user_name like '%' #{userName} '%'
</if>
<if test="age!=null">
and age = #{age}
</if>
</where>
</select>
(3)测试
@Test
public void testQueryUserListByUserNameAndAge(){
List<User> list = userMapper.queryUserListByUserNameAndAge("", 30);
for (User user : list) {
System.out.println(user);
}
}
若两个条件都输入了,则两个条件都成立
若其中一个为空,则自动去除这个条件(and)
4、Set
set标签:
可以自动添加一个set关键字,并且会将动态sql最后多余的逗号去除。
解释如下:
那么一旦在传递的参数中没有age,此时生成的sql机构就会因为多了一个逗号而报错。set会自动将动态sql最后多余的逗号去除
案例:修改用户信息,如果参数user中的某个属性为null,则自动去除。
(1)定义接口
在UserMapper接口中定义方法:
/**
* 修改用户
* @param user
*/
void updateUserSelective(User user);
(2)编写Mapper.xml
<update id="updateUserSelective">
update tb_user
<set>
<if test="userName!=null and userName.trim()!=''">
user_name = #{userName},
</if>
<if test="password!=null and password.trim()!=''">
password = #{password},
</if>
<if test="name!=null and name.trim()!=''">
name = #{Name},
</if>
<if test="age!=null">
age = #{age}
</if>
</set>
where id = #{id}
</update>
(3)测试
@Test
public void testUpdateUserSelective(){
User user = new User();
user.setUserName("admin11");
user.setPassword("654321");
user.setName("用户11");
//user.setAge(20);
user.setId(9l);
userMapper.updateUserSelective(user);
}
不缺少参数的情况:
缺少参数的情况,sql机构就会因为多了一个逗号而报错
缺少参数,但set会自动将动态sql最后多余的逗号去除
5、foreach
foreach标签:遍历集合或者数组
collection属性:接收的集合或者数组参数
item属性:集合或者数组参数中的每一个元素
separator属性:标签分隔符
open属性:以什么开始
close属性:以什么结束
案例:按照多个id查询用户信息
(1)定义接口
在UserMapper接口中定义方法:
/**
* 通过多个用户id查询多个用户
* @param ids
* @return
*/
List<User> queryUserListByIds(@Param("ids")Long[] ids);
(2)编写Mapper.xml
<select id="queryUserListByIds" resultType="User">
select * from tb_user where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
(3)测试
@Test
public void testQueryUserListByIds(){
Long [] ids = new Long[] {1l,2l,3l};
List<User> users = userMapper.queryUserListByIds(ids);
for (User user : users) {
System.out.println(user);
}
}
二、高级查询(一对一查询、一对多查询、多对多查询)
1、表介绍
tb_user:用户表
该表用于存放用户信息
tb_order:订单表
用于存放订单编号和用户id
tb_item:商品表
该表用于存放商品信息
tb_orderdetail:订单详情表
用于存放订单的详细信息,如订单编号、商品编号、商品价格等。
2、表关系说明
3、数据准备
4、一对一查询
4.1添加User属性方式
需求:通过订单编号20140921003查询出订单信息,并查询出下单人信息。
查询语句以及查询结果:
步骤一:编写接口
public interface OrderMapper {
/**
* 通过订单编号查询订单和用户
* @param orderNumber:订单编号
* @return
*/
Order queryOrderAndUserByOrderNumber(@Param("orderNumber") String orderNumber);
}
步骤二:创建Order
/**
* 订单表
*
*/
public class Order {
private Integer id;
private Long userId;
private String orderNumber;
/**建立订单order与用户user的一对一的关系*/
private User user;
@Override
public String toString() {
return "Order{" +
"id=" + id +
", userId=" + userId +
", orderNumber='" + orderNumber + '\'' +
", user=" + user +
'}';
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
}
步骤三:编写 OrderMapper.xml
注意:一旦涉及到嵌套映射,一定要设置手动设置为自动映射。不管是Order的自动映射还是User的自动映射都需要手动设置为true。
步骤四:全局配置文件管理映射文件
在mybatis-config.xml中添加代码如下:
<mapper resource="OrderMapper.xml"/>
步骤五:编写测试类
public class OrderMapperTest {
private OrderMapper orderMapper;
@Before
public void setUp() throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sessionFactory.openSession(true);
orderMapper = sqlSession.getMapper(OrderMapper.class);
}
@Test
public void queryOrderAndUserByOrderNumber() {
Order order = orderMapper.queryOrderAndUserByOrderNumber("20140921003");
System.out.println(order);
}
}
5、一对多查询
一对多查询:通过订单编号20140921001查询订单,并查询出下单人信息以及查询出订单详情。
思路:
订单 : 订单详情 = 1 : n(体现在pojo对象中,就是在Order对象中添加OrderDetail对象的集合)
SQL:查询出两条记录,除了item_id和total_price不同外,其他都一样(截图的原因,无法截全,因此只截图后边不一样的部分)。
步骤一:修改Order
在Order类添加List<OrderDetial>属性,并添加get、set方法和toString方法:
步骤二:编写接口方法
public interface OrderMapper {
/**
* 通过订单编号查询订单和用户
* @param orderNumber:订单编号
* @return
*/
Order queryOrderAndUserByOrderNumber(@Param("orderNumber") String orderNumber);
/**
* 通过订单编号查询订单、用户和订单详情
* @param orderNumber:订单编号
* @return
*/
Order queryOrderAndUserAndOrderdetailByOrderNumber(@Param("orderNumber") String orderNumber);
}
步骤三:编写statement
步骤四:测试
@Test
public void queryOrderAndUserAndOrderdetailByOrderNumber(){
Order order = orderMapper.queryOrderAndUserAndOrderdetailByOrderNumber("20140921001");
System.out.println(order);
}
注意:在使用别名查询来映射主键的时候,一定要手动在sql语句中自行定义别名。
6、多对多查询
多对多查询:通过订单号20140921001查询订单,查询出下单人信息并且查询出订单详情以及商品数据。
思路:
订单:订单详情 = 1 : n(体现在pojo对象中就是在Order对象中添加OrderDetail对象的集合)
订单详情:商品 = 1 : 1(体现在pojo对象中就是在OrderDetail对象中添加Item对象)
Sql:
#通过订单号20140921001查询订单,查询出下单人信息并且查询出订单详情中的商品数据。
步骤一:修改Orderdetail
通过订单号20140921001查询订单,查询出下单人信息并且查询出订单详情中的商品数据。
1、由于是通过Order对一查询User,因此要在Order的实体中设置对一User的属性配置,然后还要对多去查询Orderdetail,因此要在Order实体类中设置对多detailList的属性来进行查询,并添加get、set方法:之前已设置过。
2、还要通过Orderdetail去对一查询Item,因此要在Orderdetail中去设置对一Item的属性,并添加get、set方法:
步骤二:编写接口方法
public interface OrderMapper {
/**
* 通过订单编号查询订单、用户和订单详情以及商品
* @param orderNumber
* @return
*/
Order queryOrderAndUserAndOrderdetailAndItemByOrderNumber(@Param("orderNumber") String orderNumber);
}
步骤三: 编写statement
OrderMapper配置(通过在collection标签中嵌套使用association标签):
步骤四:测试
@Test
public void queryOrderAndUserAndOrderdetailAndUserByOrderNumber(){
Order order = orderMapper.queryOrderAndUserAndOrderdetailAndItemByOrderNumber("20140921001");
System.out.println(order);
}
7、resultMap的继承
8、高级查询的整理
resutlType无法帮助我们自动的去完成映射,所以只有使用resultMap手动的进行映射
resultMap:
type 结果集对应的数据类型
id 唯一标识,被引用的时候,进行指定
autoMapping 开启自动映射
extends 继承
子标签:
association:配置对一的映射
property 定义对象的属性
javaType 属性的类型
autoMapping 开启自动映射
collection:配置对多的映射
property 定义对象的属性名
javaType 集合的类型
ofType 集合中的元素类型
autoMapping 开启自动映射
三、延迟加载
实际开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加载他的账户信息。此时就是我们所说的延迟加载。
1、何为延迟加载?
延迟加载:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。。
坏处:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
association、collection实现一对一及一对多映射。association、collection具备延迟加载功能。
2、需求:
延迟加载需求:通过订单编号20140921001查询order并延迟加载user
如果通过订单编号查询order并且查询user信息,在正常情况下的查询语句应该是:
如果改成延迟加载,也就意味着,先查询order,等需要的时候再去查询user,那就相当于将上面的一条语句变成了两条语句:
1、通过订单编号查询order
2、通过查询出来的order中的user_id查询user
3、延迟加载案例
步骤一:编写接口方法
/**
* 延迟加载
* @param orderNumber
* @return
*/
Order queryOrderUserLazy(@Param("orderNumber")String orderNumber);
步骤二:编写statement
两个statement就分别相当于两条查询order和查询user的sql语句,然后通过resultMap将延迟加载的查询user的statement关联到查询查询order的statement上。使得在查询order之后,可以延迟加载user.
步骤三:开启延迟加载开关
在全局配置文件中开启延迟加载的开关:
<!-- 设置参数 -->
<settings>
<!--开启驼峰-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"></setting>
</settings>
步骤四:设置idea的Debug功能
此处注意不要使用Debug查看延迟加载语句的发送时机,因为idea工具在使用Debug查询时以为用户要查看数据,因此会立即将两条sql语句一次性执行完毕。
如果要使用Debug来演示延迟加载,需要设置如下:将自动toString的选项去掉就行。
步骤五:测试
@Test
public void queryOrderUserLazy(){
Order order = orderMapper.queryOrderUserLazy("20140921001");
//先加载order信息
System.out.println(order.getId()+"..."+order.getOrderNumber()+"..."+order.getUserId());
//延迟加载User信息
System.out.println(order.getUser());
}
加载成功;
延迟加载分析:
注意:association标签总的colunm属性,如果传递的参数只有一个的话,直接使用user_id即可,如果是多个可以使用{user_id=id,user_nam=userName}来传递。