JSD-2204-MyBatis动态sql,修改,查询-Day04

1.MyBatis的动态SQL--foreach

动态SQL:根据参数值的不同,将生成不同的SQL语句。

假设存在需求:根据若干个id删除相册数据,即批量删除。

需要执行的SQL语句大致是:

delete from pms_album where id=? or id=? or id=? ...

或者:

delete from pms_album where id in (?, ?, ?, ... ?);

当实现以上功能时,关于抽象方法,可以设计为:

int deleteByIds(Long[] ids);

或者:

int deleteByIds(Long... ids);

或者:

int deleteByIds(List<Long> ids);

在配置SQL时,需要使用到<foreach>节点对参数进行遍历:

<!-- int deleteByIds(Long[] ids); -->
<delete id="deleteByIds">
    DELETE FROM pms_album
    WHERE id IN (
    	<foreach collection="array" item="id" separator=",">
            #{id}
    	</foreach>
    )
</delete>

关于<foreach>节点的配置:

  • collection属性:当抽象方法的参数只有1个且没有添加@Param注解时,当参数是数组类型时(包括类型为可变参数时),此属性取值为array,当参数是List集合类型时,此属性取值为list
  • item属性:遍历过程中的每个元素的变量名,是自定义的名称
  • separator属性:遍历过程中各元素之间的分隔符号

练习:批量插入相册数据

需要执行的SQL语句大致是:

insert into pms_album (name,description,sort) values (?,?,?), (?,?,?), (?,?,?)

AlbumMapper中添加抽象方法:

int insertBatch(List<Album> albums);

AlbumMapper.xml中配置SQL语句:

<!-- int insertBatch(List<Album> albums); -->
<insert id="insertBatch" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO pms_album (
    	name, description, sort
    ) values 
    <foreach collection="list" item="album" separator=",">
        (#{album.name}, #{album.description}, #{album.sort})
    </foreach>
</insert>

2.使用Mybaits修改数据

通常,修改数据时,也会使用到动态SQL的机制,当传入某个字段对应的值时,SQL中才会包含修改此字段的部分,反之,如果没有传入某个字段对应的值,则SQL语句不会包含修改此字段的部分!

这样的功能可以通过动态SQL的<if>标签来实现!

假设需要实现修改相册数据,传入的参数中包含哪些数据,就修改哪些数据,不包含的部分将不会被修改。

AlbumMapper接口中添加抽象方法:

int update(Album album);

AlbumMapper.xml中配置SQL语句:

<!-- int update(Album album); -->
<update id="update">
    UPDATE pms_album
    <set>
        <if test="name != null">
            name=#{name},
        </if>
        <if test="description != null">
            description=#{description},
        </if>
        <if test="sort != null">
            sort=#{sort},
        </if>
    </set>
    WHERE id=#{id}
</update>

2.1使用Mybatis查询--统计

假设需要实现:统计相册表中的数据的数量

需要执行的SQL语句大致是:

select count(*) from pms_album

关于抽象方法:在查询时,方法的返回值类型只要求能够存入查询结果即可。

则在AlbumMapper中添加抽象方法:

int count();

然后,在AlbumMapper.xml中配置SQL语句,将使用<select>节点,此节点必须配置resultTyperesultMap这2个属性中的某1个,当使用resultType时,此属性的值取决于抽象方法的返回值类型,如果是基本数据类型(例如int等),则resultType属性的值就是类型名,如果是引用数据类型(例如StringAlbum等),则resultType属性的值就是类型的全限定名(在java.lang包下的可以省略包名)。

<!-- int count(); -->
<select id="count" resultType="int">
    SELECT count(*) FROM pms_album
</select>

如果既没有配置resultType又没有配置resultMap,将会出现以下错误:

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement 'cn.tedu.csmall.product.mapper.AlbumMapper.count'.  It's likely that neither a Result Type nor a Result Map was specified.

2.2使用Mybatis查询--根据id查询

假设需要实现:根据id查询相册详情

需要执行的SQL语句大致是:

select id, name, description, sort from pms_album where id=?

关于抽象方法的返回值类型,原则上,只需要能够“放得下”就行,所以,可以使用Album作为此次查询的返回值类型,但是,并不建议这样处理!通常,建议另创建类型,用于封装查询结果!另外创建的类型,通常并不会称之为实体类,并且,这种类型会添加一些后缀,关于后缀的使用,阿里的文档的参考

  • 数据对象:xxxDO,xxx 即为数据表名
  • 数据传输对象:xxxDTO,xxx 为业务领域相关的名称
  • 展示对象:xxxVO,xxx 一般为网页名称
  • POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO

关于以上后缀:

  • DO:Data Object
  • DTO:Data Transfer Object
  • VO:View Object / Value Object

对于本次查询,可以使用VO作为类型的后缀,完整的类名可以使用AlbumStandardVO,此类应该放在项目的根包的pojo.vo包下:

@Data
public class AlbumStandardVO implements Serializable {
    // 除了gmtCreate和gmtModified以外的所有属性
}

提示:此前涉及的“实体类编写规范”本质上是POJO的规范!

接下来,在AlbumMapper接口中添加抽象方法:

AlbumStandardVO getStandardById(Long id);

AlbumMapper.xml中配置SQL:

<!-- AlbumStandardVO getStandardById(Long id); -->
<select id="getStandardById" resultType="xx.xx.xx.AlbumStandardVO">
    select id, name, description, sort from pms_album where id=#{id}
</select>

Mybatis在封装查询结果时,会自动的将**列名(Column)属性名(Property)**匹配的结果进行封装,例如查询结果中的name值将封装到返回值对象的name属性中去,对于名称不匹配的,将放弃。

可以在配置SQL时,为查询的字段自定义列名,使得“查询结果中的列名”与“封装结果的类型中的属性名”是一致的,例如:

<!-- BrandStandardVO getStandardById(Long id); -->
<select id="getStandardById" resultType="cn.tedu.csmall.product.pojo.vo.BrandStandardVO">
    SELECT
        id, name, pinyin, logo, description,
        keywords, sort, sales, 
    
    	product_count AS productCount, 
    	comment_count AS commentCount,
        positive_comment_count AS positiveCommentCount, 
    
    	enable
    FROM pms_brand
    WHERE id=#{id}
</select>

提示:在SQL语句中,自定义别名时,AS关键字并不是必须的,只需要有1个空格即可。

除了以上做法以外,还可以在application.properties中添加配置,使得Mybatis能自动处理“全小写且使用下划线分隔的字段名对应的列名”与“驼峰命名法的属性名”之间的对应关系(例如此做法时,不必在查询时自定义别名):

mybatis.configuration.map-underscore-to-camel-case=true

或者,还可以选择自定义ResultMap,用于指导Mybatis如何封装查询结果,其基本方式是:

<resultMap id="自定义的ResultMap名称" type="封装查询结果的类型的全限定名">
    <!-- 配置 -->
</resultMap>

<select id="xxx" resultMap="自定义的ResultMap名称">
</select>

<resultMap>内部,使用<result>节点,配置其columnproperty属性,用于指定列名与属性名的对应关系,例如:

<resultMap id="自定义的ResultMap名称" type="封装查询结果的类型的全限定名">
    <result column="product_count" property="productCount" />
    <result column="comment_count" property="commentCount" />
    <result column="positive_comment_count" property="positiveCommentCount" />
</resultMap>

提示:在普通的单表查询中,列名与属性名本身就对应的部分,并不需要在<resultMap>中配置。

另外,在开发实践中,建议将查询的字段列表使用<sql>节点进行封装,然后,在配置的SQL语句中,使用<include>节点进行调用即可:

<!-- BrandStandardVO getStandardById(Long id); -->
<select id="getStandardById" resultMap="StandardResultMap">
    SELECT
        <include refid="StandardQueryFields"/>
    FROM pms_brand
    WHERE id=#{id}
</select>

<sql id="StandardQueryFields">
    id, name, pinyin, logo, description,
    keywords, sort, sales, product_count, comment_count,
    positive_comment_count, enable
</sql>

<resultMap id="StandardResultMap" 
           type="cn.tedu.csmall.product.pojo.vo.BrandStandardVO">
    <result column="product_count" property="productCount" />
    <result column="comment_count" property="commentCount" />
    <result column="positive_comment_count" property="positiveCommentCount" />
</resultMap>

当在<select>上使用resultType属性,取值却是<resultMap>的id时,将出现以下错误:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.core.NestedIOException: Failed to parse mapping resource: 'file [C:\Users\pc\IdeaProjects\jsd2204-csmall-product-teacher\target\classes\mapper\BrandMapper.xml]'; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'file [C:\Users\pc\IdeaProjects\jsd2204-csmall-product-teacher\target\classes\mapper\BrandMapper.xml]'. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'StandardResultMap'.  Cause: java.lang.ClassNotFoundException: Cannot find class: StandardResultMap

当在<select>上使用了resultMap,取值错误时(例如取值为类型的全限定名),将出现以下错误:

java.lang.IllegalArgumentException: Result Maps collection does not contain value for cn.tedu.csmall.product.pojo.vo.BrandStandardVO

2.3使用Mybatis查询数据列表

查询列表与查询某1个数据的开发过程相差不大,主要区别在于:

  • 查询列表时,需要查询的字段通常更少
  • Mybatis会自动使用List集合来封装查询到的多个数据,所以,抽象方法的返回值类型必须是List类型的

假设需要实现:查询品牌列表(不考虑分页问题)

需要执行的SQL语句大致是:

select * from pms_brand order by sort desc, pinyin, id desc

注意:如果执行的查询的结果可能超过1条(即2条或以上),必须显式的指定order by进行排序!

vo包中创建BrandListItemVO类:

@Data
public class BrandListItemVO implements Serializable {
    // id, name, logo
}

然后,在BrandMapper接口中添加抽象方法:

List<BrandListItemVO> list();

BrandMapper.xml中配置SQL:

<!-- List<BrandListItemVO> list(); -->
<select id="list" resultMap="ListItemResultMap">
    SELECT
        <include refid="ListItemQueryFields"/>
    FROM pms_brand
    ORDER BY sort DESC, pinyin, id DESC
</select>

<sql id="ListItemQueryFields">
    id, name, logo
</sql>

<resultMap id="ListItemResultMap" type="cn.tedu.csmall.product.pojo.vo.BrandListItemVO">
</resultMap>

注意:即使是查询列表,无论使用resultType,还是配置<resultMap>,关于数据类型,都只需要指定为List中的元素类型即可!

作业

完成以下数据表的功能:“根据id查询数据”(已完成的则跳过)、“根据名称统计数据数量”

  • 品牌表
  • 相册表
  • 类别表xx
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值