输入映射
输出映射
动态sql
where
foreach
set
bind
choose
trim
1.输入映射
单个参数可以用parameterType指定类型,一个对象的多个参数可以用类封装用parameterType指定。
多个参数可以通过注解方式指定每个参数的类型。
在UserMapper.xml内添加以下语句,该sql语句有两个参数。
<select id="findUser" resultType="cn.edu.jxnuss.dpc.pojo.User">
select * from tb_user where id=#{id} and username=#{username}
</select>
在UserMapper.java接口里加上findUser方法。
通过注解@Param()指定java文件里形参id到xml里id的映射关系,从而指定id的数据类型。
public User findUser(@Param("id") int id, @Param("username") String username);
测试方法和单参数基本一样
@Test
public void findUser() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int id = 10;
String username = "张三";
User user = mapper.findUser(id, username);
System.out.println(user);
sqlSession.close();
}
2.输出映射
使用resultMap关联结果集映射
单个输出参数用resultType指定类型,一个对象的多个参数接收查询结果,一般情况下,需要数据库表的列名和实体类的名字完全相同。
因为Java中命名规范和数据库的命名规范不一样,所以有种方法可以实现查询结果到复杂类型的映射,即resultMap。
数据表
DROP TABLE IF EXISTS `tb_orders`;
CREATE TABLE `tb_orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '下单用户id',
`number` varchar(32) NOT NULL COMMENT '订单号',
`createtime` datetime NOT NULL COMMENT '创建订单时间',
`note` varchar(100) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
KEY `FK_orders_1` (`user_id`),
CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `tb_user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of orders
-- ----------------------------
INSERT INTO `tb_orders` VALUES ('3', '1', '1000010', '2015-02-04 13:22:35', null);
INSERT INTO `tb_orders` VALUES ('4', '1', '1000011', '2015-02-03 13:22:41', null);
INSERT INTO `tb_orders` VALUES ('5', '10', '1000012', '2015-02-12 16:13:23', null);
cn.edu.jxnuss.dpc.pojo.Order.java
package cn.edu.jxnuss.dpc.pojo;
import java.util.Date;
/**
* @program: Mybatis20200301
* @description:
* @author: qingzhu
* @create: 2021-03-04 11:03
*/
public class Order {
int id;//主键
int userId;//下单用户id
String number;//订单号
Date createTime;//创建订单时间
String note;//备注
@Override
public String toString() {
return "Order{" +
"id=" + id +
", uerId=" + userId +
", number='" + number + '\'' +
", createTime=" + createTime +
", note='" + note + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int uerId) {
this.userId = uerId;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
<?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.edu.jxnuss.dpc.mapper.OrderMapper">
<!--id是标识,方便之后引用;type是指定映射到哪个pojo类上-->
<resultMap id="orderResult" type="cn.edu.jxnuss.dpc.pojo.Order">
<!-- 定义主键 ,非常重要。如果是多个字段,则定义多个id
property:主键在pojo中的属性名
column:查询结果中的列名 -->
<id column="id" property="id" />
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createTime"/>
<result column="note" property="note"/>
</resultMap>
<select id="findOrders" resultMap="orderResult">
select * from tb_orders
</select>
</mapper>
注意:记得在mybatis-config.xml全局配置文件中加上映射器。
<mappers>
<mapper resource="mapper/UserMapper.xml"></mapper>
<mapper resource="mapper/OrderMapper.xml"></mapper>
</mappers>
当有多个xml映射器在同一个包的时候
使用包扫描的方式批量引入Mapper接口
使用规则:
1. 接口的名称和映射文件名称除扩展名外要完全相同
2. 接口和映射文件要放在同一个目录下
<package name="cn.edu.jxnuss.dpc.mapper"/>
测试代码
package cn.edu.jxnuss.dpc.mapper;
import cn.edu.jxnuss.dpc.pojo.Order;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import static org.junit.Assert.*;
public class OrderMapperTest {
SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws IOException {
//配置文件
String resource = "mybatis-config.xml";
//加载配置文件到输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建会话工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void findOrders() {
SqlSession sqlSession = sqlSessionFactory.openSession();
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
List<Order> orders = orderMapper.findOrders();
System.out.println(orders);
sqlSession.close();
}
}
3.动态sql
通过mybatis提供的各种标签方法实现动态拼接sql。同样代码通过输入参数不同执行不同的sql语句。
where
<!--单独定义,方便重用,id唯一标识-->
<sql id="sql_where">
<!--where标签会自动向sql语句中添加where关键字,且会去掉第一个满足条件的and关键字-->
<where>
<if test="username != null and username != '' ">
and username like '%${username}%'
</if>
<if test="sex != null and sex != '' ">
and sex = #{sex}
</if>
</where>
</sql>
<select id="findUserByNameAndSex" parameterType="cn.edu.jxnuss.dpc.pojo.User" resultType="cn.edu.jxnuss.dpc.pojo.User">
select * from tb_user
<!--用include标签引用-->
<include refid="sql_where"></include>
</select>
cn.edu.jxnuss.dpc.mapper.UserMapper.java
public List<User> findUserByNameAndSex(User user);
测试代码
@Test
public void findUserByNameAndSex() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setSex("女");
user.setUsername("小芳");
List<User> users = mapper.findUserByNameAndSex(user);
System.out.println(users);
sqlSession.close();
}
测试结果
发现结果有问题,username=root,是jdbc.properties文件里对连接数据库的用户名定义,也就是说mybatis-config.xml对username的赋值覆盖了传入的参数。所以将jdbc.properties文件里的变量重新定义:
#jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useServerPrepStmts=true&cachePrepStmts=true&rewriteBatchedStatements=true
jdbc.username=root
jdbc.password=1234
同样还有mybatis-config.xml也要修改
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"></mapper>
<mapper resource="mapper/OrderMapper.xml"></mapper>
</mappers>
</configuration>
1)
代码:
@Test
public void findUserByNameAndSex() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setSex("女");
user.setUsername("小芳");
List<User> users = mapper.findUserByNameAndSex(user);
System.out.println(users);
sqlSession.close();
}
结果
2)
代码
@Test
public void findUserByNameAndSex() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setSex("");
user.setUsername("小芳");
List<User> users = mapper.findUserByNameAndSex(user);
System.out.println(users);
sqlSession.close();
}
结果
3)
代码
@Test
public void findUserByNameAndSex() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setSex("女");
user.setUsername("");
List<User> users = mapper.findUserByNameAndSex(user);
System.out.println(users);
sqlSession.close();
}
结果
4)
代码
@Test
public void findUserByNameAndSex() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setSex("");
user.setUsername("");
List<User> users = mapper.findUserByNameAndSex(user);
System.out.println(users);
sqlSession.close();
}
结果
可以看出函数对不同输入参数执行的sql语句不同。
foreach
向sql传递数组或List,mybatis使用foreach解析。
如果要将List中的id,执行以下语句,那么就要使用到foreach。
SELECT * FROM USERS WHERE id IN (10,89,16)
cn.edu.jxnuss.dpc.mapper.UserMapper
public List<User> findUserByIds(@Param("ids") List<Integer> ids);
UserMapper.xml
<select id="findUserByIds" resultType="cn.edu.jxnuss.dpc.pojo.User">
select * from tb_user
<where>
<if test="ids != null">
<!--
foreach:循环传入的集合参数
collection:传入的集合的变量名称
item:每次循环将循环出的数据放入这个变量中
open:循环开始拼接的字符串
close:循环结束拼接的字符串
separator:循环中拼接的分隔符
-->
<foreach collection="ids" item="id" open="id in (" close=")" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
测试代码
@Test
public void findUserByIds() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(10);
list.add(24);
List<User> users = mapper.findUserByIds(list);
System.out.println(users);
sqlSession.close();
}
结果
输入输出参数一直写完全的类名很麻烦,mybatis提供了类型别名。
typeAliases(类型别名)
可以将parameterType、resultType中指定的类型通过别名引用。以便简化代码。
在mybatis全局配置文件里添加别名
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"></properties>
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias alias="user" type="cn.edu.jxnuss.dpc.pojo.User"/>
<!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以)
<package name="cn.edu.jxnuss.dpc.pojo"/>
-->
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"></mapper>
<mapper resource="mapper/OrderMapper.xml"></mapper>
</mappers>
</configuration>
注意:标签的顺序一定要对。
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
之后的引用类型就可以写别名的,运行效果是一样的。
<select id="findUser" resultType="User">
select * from tb_user where id=#{id} and username=#{username}
</select>
set
set 标签元素主要是用在更新操作的时候,它的主要功能和 where 标签元素其实是差不多的,主要是在包含的语句前输出一个 set,然后如果包含的语句是以逗号结束的话将会把该逗号忽略,如果 set 包含的内容为空的话则会出错。有了 set 元素就可以动态的更新那些修改了的字段。
用set语句对修改User方法改进
<!--修改User-->
<update id="updateUserById" parameterType="cn.edu.jxnuss.dpc.pojo.User">
update tb_user
<set>
<if test="username != null and username != '' ">
username = #{username},
</if>
<if test="sex != null and sex != '' ">
sex = #{sex},
</if>
<if test="birthday != null">
birthday = #{birthday}
</if>
</set>
where id=#{id}
</update>
测试代码
@Test
public void updateUserById() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(22);
user.setUsername("王三");
mapper.updateUserById(user);
sqlSession.commit();
sqlSession.close();
}
结果
可以看到sql语句只对username进行了修改,sex和birthday因为是空,所以没有修改。
bind
之前进行模糊查询的时候,要使用${}拼接,而这样可能造成sql注入。bind标签可以解决这样的问题。
<!--通过姓名模糊查询-->
<select id="findUserByname" parameterType="java.lang.String" resultType="cn.edu.jxnuss.dpc.pojo.User">
<bind name="bindName" value="'%' + username + '%'"/>
select * from tb_user
where username like #{bindName}
</select>
cn.edu.jxnuss.dpc.mapper.UserMapper
public List<User> findUserByname(@Param("username") String username);
测试代码
@Test
public void findUserByname() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.findUserByname("王");
System.out.println(users);
sqlSession.close();
}
结果
choose
choose标签就和switch一样,当不想执行所有的条件语句时,可以使用choose标签,choose从上到下判断条件,当一个条件满足的时候跳出choose,不执行下面的判断条件,otherwise相当于default。
<!--修改User2-->
<update id="updateUserById2" parameterType="cn.edu.jxnuss.dpc.pojo.User">
update tb_user
<set>
<choose>
<when test="username != null and username != '' ">
username = #{username}
</when>
<when test="sex != null and sex != '' ">
sex = #{sex}
</when>
<otherwise>
birthday = #{birthday}
</otherwise>
</choose>
</set>
where id=#{id}
</update>
cn.edu.jxnuss.dpc.mapper.UserMapper
public void updateUserById2(User user);
测试代码
@Test
public void updateUserById2() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(22);
user.setUsername("王三");
user.setSex("男");
mapper.updateUserById2(user);
sqlSession.commit();
sqlSession.close();
}
结果
可以看到因为username不为空,执行第一个when里的内容,后面的不管是否为true都不执行。
trim
mybatis的trim标签用于去除sql语句中多余的and关键字,逗号,或者给sql语句前拼接 “where“、“set“以及“values(“ 等前缀,或者添加“)“等后缀,可用于选择性插入、更新、删除或者条件查询等操作。
trim标签里涉及到的属性
prefix | 给sql语句拼接的前缀 |
---|---|
suffix | 给sql语句拼接的后缀 |
prefixOverrides | 去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定 |
suffixOverrides | 去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定 |
假设有如下语句
<select id="findUserByNameAndSex2" parameterType="User" resultType="User">
select * from tb_user
<where>
<if test="username != null and username != '' ">
and username like '%${username}%'
</if>
<if test="sex != null and sex != '' ">
and sex = #{sex}
</if>
</where>
</select>
1)如果第一个条件满足,则会拼接成
select * from tb_user where and username like ‘%${username}%’
where标签会把第一个满足条件的and去掉,即变成
select * from tb_user where username like ‘%${username}%’
2)没有一个条件满足
select * from tb_user where
where标签在没有条件满足的情况下不会有where关键字
select * from tb_user
trim标签也能实现这样的功能
<select id="findUserByNameAndSex2" parameterType="User" resultType="User">
select * from tb_user
<trim prefix="where" prefixOverrides="and">
<if test="username != null and username != '' ">
and username like '%${username}%'
</if>
<if test="sex != null and sex != '' ">
and sex = #{sex}
</if>
</trim>
</select>
其他属性也可以灵活运用,使得代码运行更安全。