今天接着上期mybatis相关知识进行讲解,今天主要是讲解mybatis和数据库相关的映射,标签和SQL编写等。
会结合实际业务和代码进行讲解。
1 占位符和传参的相关问题
先来看两条xml的SQL。第一条SQL从id名称可知,是根据id删除数据,数据表t_address地址表,参数是Integer类型的id。第二条SQL根据年龄查询用户,参数是Integer类型的age。
<delete id="deleteById" parameterType="Integer">
delete from t_address where id=#{id}
delete>
<mapper namespace="com.boger.dao.UserMapper">
<select id="getUsersByAge" resultType="User">
SELECT * FROM User WHERE age = ${age}
select>
mapper>
那么来看 两者传参的区别:
#{}是占位符,预编译处理;${}是拼接符,字符串替换,没有预编译处理。
Mybatis在处理#{}时,会将SQL中的#{}替换为?号,调用PreparedStatement的set方法来赋值。#{} 可以有效的防止SQL注入,提高系统安全性;${} 不能防止SQL 注入
单个参数,我们可以这样传递,那么是多个参数该怎么传递呢?
传递多个参数有以下几种方法:
业务需求: 根据院系Id和用户名查询用户。 都是在UserMapper.java文件中定义方法selectUser。
(1) 按照顺序进行传参
public User selectUser(String name, int deptId);
<select id="selectUser" resultMap="UserResultMap">
select * from user where user_name = #{0} and dept_id = #{1}
select>
#{}里面的数字代表传入参数的顺序。这种方法表达不直观,一旦顺序调整就容易出错。因此,不建议使用。
(2) 使用@Param注解进行传递参数
public User selectUser(@Param("userName") String name, int @Param("deptId")deptId);
<select id="selectUser" resultMap="UserResultMap">
select * from user where user_name = #{userName} and dept_id = #{deptId}
select>
#{}里面的名称对应的是注解@Param括号里面修饰的名称。
可以看到在参数不多的情况推荐使用。
(3) 将参数封装到map传参
public User selectUser(Map<String, Object> paramsMap);
<select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
select * from user where user_name = #{userName} and dept_id = #{deptId}
select>
#{}里面的名称对应的是Map里面的key名称。
可以看到这种方法适合传递多个参数(尤其是三个及其三个以上),同时参数能灵活传递。
(4) 通过Java实体类(javaBean)传参。
public User selectUser(User user);
<select id="selectUser" parameterType="com.jourwon.pojo.User"resultMap="UserResultMap">
select * from user where user_name = #{userName} and dept_id = #{deptId}
select>
参数User就是java实体类,#{}里面的名称对应的是User类里面的成员属性。此方法扩展不易,但代码可读性强,推荐使用。
2 模糊匹配的SQL
推荐常使用的两种模糊查询写法。
第一种: “%”#{param}“%”
第二种: CONCAT(’%’,#{param},’%’) 使用CONCAT()函数
例如: 业务要求: 根据输入的字符串用户名查询用户。在UserMapper.java文件定义方法selectByCondition。
User selectByCondition(String queryString);
第一种写法:
<select id="selectByCondition" parameterType="String" resultType="com.boger.User">
select * from t_user
where queryString like "%"#{value}"%"
select>
第二种写法:
<select id="selectByCondition" parameterType="String" resultType="com.boger.User">
select * from t_user
where queryString CONCAT(’%’,#{value},’%’)
select>
3 Mybatis如何获取生成的主键
在标签中使用 useGeneratedKeys 和 keyProperty 两个属性来获取自动生成的主键值。
示例:
<insert id=”AddUser” usegeneratedkeys=”true” keyproperty=”id”>
insert into user(name) values (#{name})
insert>
当然,除了使用 useGeneratedKeys 和 keyProperty,还可以使用下面的标签。
<selectKey resultType="int" order="AFTER" keyProperty="id">
SELECT LAST_INSERT_ID()
selectKey>
于是上面的写法,就变为了下面的完整的SQL.
<insert id=”AddUser” >
<selectKey resultType="int" order="AFTER" keyProperty="id">
SELECT LAST_INSERT_ID()
selectKey>
insert into user(name) values (#{name})
insert>
4 Mybatis的批量操作
在 MyBatis 中执行批量操作,可以使用批量插入(batchInsert)、批量更新(batchUpdate)等方法来提高数据库操作的效率。下面用一个批量插入学生到数据库作为案例。
<mapper namespace="com.boger.dao.UserMapper">
<insert id="batchInsertUsers" parameterType="java.util.List">
INSERT INTO User (name, age) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.name}, #{user.age})
foreach>
insert>
mapper>
使用的是foreach标签。foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。
上述sql语句中,collection表示的是接收的集合数据,item表示的是集合中每一个元素进行迭代时的别名。separator 表示在每次进行迭代之间以什么符号作为分隔符,常用“,”;。
这里collection的属性需要特别注意:
传入的是单参数且参数类型是一个List的时候,collection属性值为list
传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
传入的参数是多个的时候,就需要把它们封装成一个Map,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key。
5 当实体类中的属性名和数据库表中的字段名不一样的解决方案
(1) 通过在查询的SQL语句中定义字段名的别名,让字段名的别名和实体类的属性名一致
(2) 通过resultMap标签来映射字段名和实体类属性名的一一对应的关系,其中,property表示实体类属性,column表示数据库列名。通常这里容易忽视主键id的映射。id映射需要单独映射。如下图:
6 mapper接口的原理
通常我们在建立好mapper接口(也称为Dao接口),就需要建立好对应的mapper的xml。例如,UserMapper.java或者UserDao.java。就会有对应的UserMapper.xml或者UserDao.xml。我们注意到,这个接口,没有实现类。
mapper接口的原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao(mapper)接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。
同时,也需要注意,mapper对应的xml文件,里面通常会设置命名空间(namespace)。即
<mapper namespace="com.boger.DictMapper"> mapper>
如果没有配置命名空间,id就不能重复。但是,在实际运用中,我们都会配置命名空间,让代码更规范。此时id就能重复。举个例子,有UserMapper TeacherMapper,我们就会创建对应的UserMapper.xml和TeacherMapper.xml。里面配置命名空间,但UserMapper和TeacherMapper里面都可以有id为selectAll的方法(查询所有)。只不过一个查询所有用户,一个查询所有讲师。职责更明确。
7 Mybatis的动态SQL
动态SQL是MyBatis的强大特性之一, 基于功能强大的OGNL表达式。
动态SQL主要是来解决查询条件不确定的情况,在程序运行期间,根据提交的条件动态的完成查询。
mybatis常用的动态SQL标签:
if choose when otherwise foreach set where
下面通过举例来说明:
案例1:
if:用于条件判断,根据条件决定是否包含某部分SQL语句
where: 使用where标签生成WHERE子句。
<mapper namespace="com.boger.dao.UserMapper">
<select id="getUsersByCondition" resultType="User">
SELECT * FROM User
<where>
<if test="name != null">
AND name = #{name}
if>
<if test="age != null">
AND age = #{age}
if>
where>
select>
mapper>
如果name参数不为null,则在SQL语句中添加AND name = #{name}的条件;如果age参数不为null,则在SQL语句中添加AND age = #{age}的条件。这样可以根据不同的条件生成不同的SQL查询语句。
案例2:
choose、when、otherwise元素:类似于Java中的switch语句,用于根据不同的条件选择执行不同的分支。
<mapper namespace="com.boger.dao.UserMapper">
<select id="getUsersByCondition" resultType="User">
SELECT * FROM User
<where>
<choose>
<when test="name != null">
AND name = #{name}
when>
<when test="age != null">
AND age = #{age}
when>
<otherwise>
AND status = 'active'
otherwise>
choose>
where>
select>
mapper>
如果name参数不为null,则在SQL语句中添加AND name = #{name}的条件;如果age参数不为null,则在SQL语句中添加AND age = #{age}的条件;如果以上条件都不满足,则在SQL语句中添加AND status = 'active’的条件。
案例3:
foreach元素:用于遍历集合或数组,并将元素应用到SQL语句中 ,在上述批量插入已讲解,这里不多赘述。
案例4:
使用 set 标签可以方便地生成 UPDATE 语句中的 SET 子句,根据不同的条件动态设置更新的字段。
<mapper namespace="com.boger.dao.UserMapper">
<update id="updateUser" parameterType="User">
UPDATE User
<set>
<if test="name != null">
name = #{name},
if>
<if test="age != null">
age = #{age},
if>
set>
WHERE id = #{id}
update>
mapper>
如果传入的 User 对象中的 name 属性不为 null,则将 name 字段包含在 SET 子句中;如果传入的 User 对象中的 age 属性不为 null,则将 age 字段包含在 SET 子句中。
8 Mybatis执行一对一和一对多的查询
实现方式:
(1) 单独发送一个SQL去查询关联对象,赋给主对象,然后返回主对象
(2) 嵌套查询和关联映射。 (推荐使用)
首先是一对一 使用标签 association
案例如下: 假设有两个表,User 表和 Address 表,一个用户对应一个地址。
<mapper namespace="com.boger.dao.UserMapper">
<select id="getUserWithAddress" resultMap="userWithAddressResultMap" parameterType="Integer">
SELECT u.id, u.name, u.age, a.id AS address_id, a.city, a.street
FROM User u
LEFT JOIN Address a ON u.id = a.user_id
WHERE u.id = #{userId}
select>
<resultMap id="userWithAddressResultMap" type="User">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="age" column="age" />
<association property="address" javaType="Address">
<id property="id" column="address_id" />
<result property="city" column="city" />
<result property="street" column="street" />
association>
resultMap>
mapper>
getUserWithAddress 方法通过 LEFT JOIN 连接 User 表和 Address 表,查询指定用户及其地址信息。
使用 resultMap 标签定义了 userWithAddressResultMap 结果映射,将查询结果映射到 User 对象,并通过 association标签将 Address 对象关联到 User 对象的 address 属性上。
java代码如下:
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserWithAddress(1);
执行 getUserWithAddress 方法,将会查询指定 userId为1 的用户及其关联的地址信息,将结果映射到 User 对象中。
一对多使用标签collection
案例如下: 假设有两个表,User 表和 Order 表,一个用户可以有多个订单。
<mapper namespace="com.boger.dao.UserMapper">
<select id="getUserWithOrders" resultMap="userWithOrdersResultMap">
SELECT u.id, u.name, u.age, o.id AS order_id, o.order_number, o.order_date
FROM User u
LEFT JOIN Orders o ON u.id = o.user_id
WHERE u.id = #{userId}
select>
<resultMap id="userWithOrdersResultMap" type="User">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="age" column="age" />
<collection property="orders" ofType="Order">
<id property="id" column="order_id" />
<result property="orderNumber" column="order_number" />
<result property="orderDate" column="order_date" />
collection>
resultMap>
mapper>
getUserWithOrders 方法通过 LEFT JOIN 连接 User 表和 Order 表,查询指定用户及其订单信息。
使用 resultMap标签定义了 userWithOrdersResultMap 结果映射,将查询结果映射到 User 对象,并通过 collection标签将多个 Order 对象关联到 User 对象的 orders 属性上。
Java 代码如下:
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserWithOrders(1);
执行 getUserWithOrders 方法,将会查询指定 userId 为1的用户及其关联的订单信息,将结果映射到 User 对象中,并通过 orders 属性获取用户的订单列表。
关于mybatis相关的SQL编写 标签大致等就差不多到此为止了。欢迎收看,下期再见。