1. 插入数据时获取自动编号的id
如果某张表的id被设计为自动编号的,在插入数据时,还可以获取自动编号的id值!
在配置SQL的<insert>
标签上,配置useGeneratedKeys
和keyProperty
属性即可:
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
此处省略插入数据的SQL语句
</insert>
以上配置中,useGeneratedKeys="true"
表示“需要获取自动编号的id值”,而keyProperty="id"
表示将id值保存到参数对象(调用插入数据的方法时使用的参数)的id
属性中。
2. 根据id删除数据
删除数据的开发步骤与插入数据的相同,只是配置SQL时使用的标签应该为<delete>
。
【需求】根据id删除某个品牌数据。
则在BrandMapper
接口中添加抽象方法:
int deleteById(Long id);
然后,在BrandMapper.xml
中配置SQL:
<delete id="deleteById">
DELETE FROM pms_brand WHERE id=#{id}
</delete>
最后,在BrandMapperTests
中编写并执行测试:
@Test
void testDeleteById() {
Long id = 1L;
int rows = mapper.deleteById(id);
System.out.println("根据id删除品牌完成,受影响的行数=" + rows);
}
3. 动态SQL:根据若干个id删除数据
动态SQL:允许根据参数不同,来生成不同的SQL语句。
【需求】根据若干个id删除品牌数据
需要执行的SQL语句大致是:
DELETE FROM pms_brand WHERE id=? OR id=? …… OR id=?;
DELETE FROM pms_brand WHERE id IN (?, ?, .... ?);
在以上SQL中,需要被删除的数据的id的数量是不确定的!
在实现此需求时,抽象方法可以设计为:
int deleteByIds(List<Long> ids);
int deleteByIds(Long[] ids);
int deleteByIds(Long... ids); // deleteByIds(1,2,3,4,5)
在配置SQL时,需要使用到<foreach>
标签对参数进行遍历:
<!-- int deleteByIds(List<Long> ids); -->
<delete id="deleteByIds">
DELETE FROM pms_brand WHERE id IN (
<foreach collection="list" item="id" separator=",">
#{id}
</foreach>
)
</delete>
关于<foreach>
标签的属性:
collection
:表示被遍历的参数对象,当抽象方法的参数只有1个时,如果参数类型是List
,则此属性值为list
,如果参数类型是数组(或可变参数),则此属性值为array
item
:遍历过程中的每个元素的名称,是自定义的名称,并且,在<foreach>
标签内部,使用#{}
时的名称也就是此属性的值(此处自定义的名称)separator
:遍历过程中在值之前添加的分隔符号
完成后,在BrandMapperTests
中编写并执行测试:
@Test
void testDeleteByIds() {
List<Long> ids = new ArrayList<>();
ids.add(2L);
ids.add(6L);
ids.add(7L);
int rows = mapper.deleteByIds(ids);
System.out.println("根据id批量删除品牌完成,受影响的行数=" + rows);
}
4. 动态SQL:修改数据
【需求】根据id修改品牌的数据,参数中传入了哪些属性,就修改对应的那些字段的值
需要执行的SQL语句大致是:
update pms_brand set name=?, pinyin=?, logo=?, description=? ....(修改其它字段的值) where id=?
则抽象方法可以设计为:
int updateById(Brand brand);
然后,配置SQL语句:
<update id="updateById">
UPDATE
pms_brand
<set>
<if test="name != null">
name=#{name},
</if>
<if test="pinyin != null">
pinyin=#{pinyin},
</if>
<if test="logo != null">
logo=#{logo},
</if>
<if test="description != null">
description=#{description},
</if>
<if test="keywords != null">
keywords=#{keywords},
</if>
<if test="sort != null">
sort=#{sort},
</if>
<if test="sales != null">
sales=#{sales},
</if>
<if test="productCount != null">
product_count=#{productCount},
</if>
<if test="commentCount != null">
comment_count=#{commentCount},
</if>
<if test="positiveCommentCount != null">
positive_comment_count=#{positiveCommentCount},
</if>
<if test="enable != null">
enable=#{enable},
</if>
</set>
WHERE
id=#{id}
</update>
以上代码中,使用到了2个标签:
<if>
:用于对参数的值进行判断,从而决定SQL语句中是否包含<if>
子级的SQL片段<set>
:用于取代SET
关键字,通常结合若干个<if>
一起使用,可以去除更新的SQL语句中的字段列表与值最后多余的逗号
注意:<if>
标签并没有匹配的类似else
的标签,如果需要实现类似Java代码中的if...else...
的效果,可以:
<if test="某条件">
满足条件时的SQL片段
</if>
<if test="与以上完全相反的条件">
满足本if时的SQL片段
</if>
以上示例可以实现类似if...else...
的效果,但是,更像是if...
与另一个if...
,本质上是执行了2次判断的!
另外,还可以使用<choose>
系列标签,真正的实现类似if...else...
的效果:
<choose>
<when test="判断条件">
满足条件时的SQL片段
</when>
<otherwise>
不满足条件时的SQL片段
</otherwise>
</choose>
5. 统计查询
【需求】统计品牌表中的数据的数量
需要执行的SQL语句大致是:
SELECT count(*) FROM pms_brand;
在BrandMapper
接口中添加抽象方法:
int count();
在BrandMapper.xml
中配置SQL语句:
<select id="count" resultType="int">
SELECT count(*) FROM pms_brand
</select>
6. 查询最多1条数据
【需求】根据id查询品牌详情
需要执行的SQL语句大致是:
SELECT
id, name, pinyin, logo, description,
keywords, sort, sales, product_count, comment_count,
positive_comment_count, enable
FROM
pms_brand
WHERE
id=?
通常,在处理查询时,并不建议使用实体类型作为查询结果,因为绝大部分查询都不需要查询表中所有的字段,如果使用实体类型,必然导致查询结果对象调用某些Getter时得到的结果会是null
,并且,这些Getter的返回结果永远会是null
。
建议使用其它的POJO类型作为封装查询结果的类型!
常见的POJO:
DO
:Data ObjectDTO
:Data Transfer ObjectVO
:View Object / Value Object
关于POJO的使用:
阿里巴巴Java开发手册
【参考】
领域模型命名规约
1) 数据对象:xxxDO,xxx 即为数据表名。
2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
3) 展示对象:xxxVO,xxx 一般为网页名称。
4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。
另外:
阿里巴巴Java开发手册
【强制】
类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DO / BO / DTO / VO / AO
正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
此次查询时,应该在项目的根包下,创建pojo.vo.BrandStandardVO
类:
@Data
public class BrandStandardVO implements Serializable {
private Long id;
private String name;
private String pinyin;
private String logo;
private String description;
private String keywords;
private Integer sort;
private Integer sales;
private Integer productCount;
private Integer commentCount;
private Integer positiveCommentCount;
private Integer enable;
}
在BrandMapper
接口中添加抽象方法:
BrandStandardVO getStandardById(Long id);
Mybatis会自动的将查询到的结果集中的数据封装到定义的返回结果类型中,但是,在默认情况下,只能处理列名(Column)与属性名(Property)一致的情况!在规范的软件开发中,推荐使用<resultMap>
来配置列与属性的映射关系:
<resultMap id="StandardResultMap" type="cn.tedu.csmall.product.pojo.vo.BrandStandardVO">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="product_count" property="productCount"/>
<result column="comment_count" property="commentCount"/>
<result column="positive_comment_count" property="positiveCommentCount"/>
</resultMap>
关于以上<resultMap>
的配置:
id
属性:是自定义的名称,在<select>
标签中的resultMap
属性的值就是对应的<resultMap>
的id
值type
属性:用于封装查询结果的类的全限定名<id>
子标签:用于配置主键的列、属性的映射关系<result>
子标签:用于配置普通(不是主键,也不是一对多、多对多的关联查询)的列、属性的映射关系
另外,还建议使用<sql>
标签封装查询的字段列表,此标签需要与<include>
标签配合使用,例如:
<select id="getStandardById" resultMap="StandardResultMap">
SELECT
<include refid="StandardQueryFields"/>
FROM
pms_brand
WHERE
id=#{id}
</select>
<sql id="StandardQueryFields">
<if test="true">
id, name, pinyin, logo, description,
keywords, sort, sales, product_count, comment_count,
positive_comment_count, enable
</if>
</sql>
7. 关于IntelliJ IDEA错误的预判
问题1:使用Lombok后,不会自动提示Setter和Getter,且使用相当方法后提示错误
在IntelliJ IDEA中安装Lombok插件即可。
问题2:使用Mybatis时,尝试自动装配Mapper接口的对象时提示错误
- 【推荐】在Mapper接口上添加
@Repository
注解即可 - 【不推荐】使用
@Resource
替换@Autowired
另外,不使用@MapperScan
,而是在每个Mapper接口上使用@Mapper
也可以解决此问题!
问题3:在配置SQL的XML中,使用<sql>
节点封装字段列表时提示错误
-
【不推荐】在IntelliJ IDEA中进行配置,不检查SQL
-
【推荐】使用某种合法的、不影响当前代码运行的代码片段,“骗”过IntelliJ IDEA即可,例如:
<sql id="StandardQueryFields"> <if test="true"> id, name, pinyin, logo, description, keywords, sort, sales, product_count, comment_count, positive_comment_count, enable </if> </sql>
作业
实现:
- 统计类别的数量
- 统计相册的数量
- 根据id查询类别详情
- 根据id查询相册详情