[NeedBox项目总结]---MyBatis小结

一、MyBatis的执行流程

  1. 首先要有MyBatis配置文件,包括MyBatis全局配置文件和MyBatis映射文件。
  2. MyBatis通过读取配置文件信息,构造出SqlSessionFactory,即会话工厂。
  3. 通过SqlSessionFactory会话工厂创建SqlSession会话,SqlSession的作用是操作数据库。
  4. SqlSession本身不能直接操作数据库,它是通过底层的Executor执行器来操作数据库的。
  5. Executor执行器要处理的SQL信息是封装到一个底层对象MappedStatement中。该对象包括:SQL语句、输入参数映射信息、输出结果集映射信息。其中输入参数和输出结果的映射类型包括Java的简单类型、HashMap集合对象、POJO对象类型。

二、自动映射和驼峰映射

MyBatis提供了自动映射功能,在默认情况下自动映射功能是开启的,使用它的好处在于能有效减少大量的映射配置,从而减少工作量。
在全局配置文件中的setting标签中有两个可以配置的选项,它们是控制自动映射和驼峰映射的开关:

  • autoMappingBehavior
  • mapUnderscoreToCamelCase
  1. 自动映射,autoMappingBehavior选项的取值范围是:
  • NONE 不进行自动映射
  • PARTIAL 默认值,只对没有嵌套结果集进行自动映射
  • FULL 对所有结果集进行自动映射,包括嵌套结果集

默认情况下,就是PARTIAL,如果编写的SQL列名和POJO属性名保持一致,就会形成自动映射。

  1. 驼峰映射,mapUnderscoreToCamelCase

如果代码中都严格按照驼峰命名法(比如数据库字段名user_name,则POJO属性名为userName),那么只要在配置项中把mapUnderscoreToCamelCase设置为true即可。

三、#{…}和${…}的区别

MyBatis的Mapper.xml语句中parameterType向SQL语句传参有两种方式:#{…} 和 ${…}
#{…}表示一个占位符号,在预编译处理时,会把参数部分用一个占位符 ? 代替
${…}表示一个拼接符号,在动态解析过程中,简单的进行字符串替换,会导致SQL注入
举个栗子~

① SELECT * FROM tb_user WHERE user_name = #{username};
② SELECT * FROM tb_user WHERE user_name = ${username};

①预编译后,会变成SELECT * FROM tb_user WHERE user_name = ?;
②在动态解析的时候,会传入参数字符串,SELECT * FROM tb_user WHERE user_name = ‘duck’;

所以它们的区别就是,#{…} 这种取值是编译好SQL语句再取值,${…} 这种是取值以后再去编译SQL语句

  • #{} 能够很大程度防止SQL注入, ${} 无法防止SQL注入
  • $方式一般用于传入数据库对象,例如传入表名
  • 一般能用#的就别用$

四、获取自增主键值和非自增主键值

我们在数据库表设计的时候,一般都会在表中设计一个自增的id作为表的主键。这个id也会关联到其它表的外键。这就要求往表中插入数据时能返回表的自增id,用这个ID去给关联表的字段赋值。有两种方法可以获取自增主键值:使用sql语句和添加属性

  1. 使用sql语句
<insert id="insertRole" parameterType="cn.entity.Role">
	INSERT INTO t_role(role_name, role_desc) VALUES(#{roleName}, #{roleDesc})
    <selectKey keyProperty="roleId" order="AFTER" resultType="int">
    	SELECT LAST_INSERT_ID()
    </selectKey>
</insert>
  • SELECT LAST_INSERT_ID():得到刚刚insert进去记录的主键值,只适用于自增主键
  • keyProperty:将得到的主键值设置到parameterType指定对象的属性中
  • order:SELECT LAST_INSERT_ID()相对于insert语句的执行顺序
  1. 添加属性
<insert id="insertRole" parameterType="cn.entity.Role"
		 useGeneratedKeys="true" keyProperty="roleId">
	INSERT INTO t_role(role_name, role_desc) VALUES(#{roleName}, #{roleDesc})
</insert>
  • useGeneratedKeys:采用JDBC的Statement对象的getGeneratedKeys方法返回主键
  • keyProperty:将得到的主键值设置到parameterType指定对象的属性中

那么非自增主键值应该怎么获取呢?使用Mysql的uuid()

<insert id="insertRole" parameterType="cn.entity.Role">
    <selectKey keyProperty="roleId" order="BEFORE" resultType="String">
    	SELECT UUID()
    </selectKey>
    INSERT INTO t_role(role_id, role_name, role_desc) VALUES(#{roleId}, #{roleName}, #{roleDesc})
</insert>
  • SELECT UUID():生成主键,将主键设置到对象的roleId属性中
  • 在执行insert时,从对象中取出roleId属性值

五、传递多个参数的4种方式

现实的需求中常常需要传递多个参数,比如订单可以通过订单编号查询,也可以根据订单名称、日期或者价格等参数进行查询等等。假设通过role_name和role_desc对角色进行查询,这样就要传递两个参数了~

1. 顺序传参法
public List<Role> findRoleByOrder(String roleName, String roleDesc);
<select id="findRoleByOrder" resultType="Role">
	SELECT * FROM tb_role
	WHERE role_name=#{0} AND role_desc=#{1}
</select>

#{}里面的数字代表传入参数的顺序~
缺点:SQL语句表达不直观,且一旦参数顺序调整,容易出错

2. 使用Map传参
public List<Role> findRoleByMap(Map<String, Object> parameterMap);

此时传递给映射器的是一个map对象,使用它在SQL中设置对应的参数

<select id="findRoleByMap" parameterType="Map" resultType="Role">
	SELECT * FROM tb_role
	WHERE role_name=#{roleName} AND role_desc=#{roleDesc}
</select>

#{}里面的名称对应的是Map里面的key名称~

RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Map<String, Object> parameterMap = new HashMap<String, Object>();
parameterMap.put("roleName", "1");
parameterMap.put("roleDesc", "1");
Role role = roleMapper.findRoleByMap(parameterMap);

缺点:

  • Map是一个键值对应的集合,使用者要通过阅读它的键,才能明了其作用
  • 使用Map不能限定其传递的数据类型,因此业务性质不强,可读性差。
3. 使用注解@Param传参
public List<Role> findRoleByAnnotation(@Param("roleName") String roleName,
									     @Param("roleDesc") String roleDesc);
<select id="findRoleByAnnotation" resultType="Role">
	SELECT * FROM tb_role
	WHERE role_name=#{roleName} AND role_desc=#{roleDesc}
</select>

#{}里面的名称对应的是注解@Param括号里指定的名称~
此时,代码的可读性大大提高~推荐使用!

4. 通过JavaBean传参

先定义一个关于参数的POJO——RoleParams

package cn.entity.RoleParams;
public class RoleParams {
	private String roleName;
	private String roleDesc;
	/** setter and getter **/
}

此时的接口方法:

public List<Role> findRoleByBean(RoleParams roleParams);

#{}里面的名称对应的是RoleParams类里面的成员属性~

<select id="findRoleByBean" parameterType="cn.entity.RoleParams" resultType="Role">
	SELECT * FROM tb_role
	WHERE role_name=#{roleName} AND role_desc=#{roleDesc}
</select>

引入JavaBean定义的属性作为参数,然后查询:

RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
RoleParams roleParams = new RoleParams();
roleParams.setRoleName("1");
roleParams.setRoleDesc("1");
Role role = roleMapper.findRoleByBean(roleParams);

这种方法很直观,但缺点是需要建一个实体类,扩展不容易,需要加属性,看情况使用。

总结:

  • 使用顺序传参和Map传参导致了业务可读性的丧失,导致后续扩展和维护的困难,果断废弃。
  • 使用注解@Param传参,受到参数个数的影响。当参数个数<=5时,推荐使用。当参数个数大于5,将给调用带来困难,此时不推荐。
  • 当参数个数大于5,建议使用JavaBean方式。

六、使用resultMap进行关联查询

MyBatis中使用resultMap完成高级输出结果映射。
什么是高级映射?高级映射就是多表关联映射:

  • 将关联查询的列映射到一个pojo属性中(一对一)
  • 将关联查询的列映射到一个List<pojo>中(一对多)

以项目中的实体类为例:
Product商品类里除了一些简单类型的成员变量,还有Shop实体类和ProductCategory实体类对象的成员变量,还有ProductImg的List集合成员变量。

public class Product {
    private Long productId;
    private String productName;
    private String productDesc;
    private String imgAddr;
    private String normalPrice;
    private String promotionPrice;
    private Integer priority;
    private Date createTime;
    private Date lastEditTime;
    private Integer enableStatus;
    
    private List<ProductImg> productImgList;
    private ProductCategory productCategory;
    private Shop shop;
}

对应的tb_product表结构是这样的:

CREATE TABLE `tb_product` (
	`product_id` int(100) NOT NULL AUTO_INCREMENT,
	`product_name` varchar(100) NOT NULL,
	`product_desc` varchar(2000) DEFAULT NULL,
	`img_addr` varchar(2000) DEFAULT '',
	`normal_price` varchar(100) DEFAULT NULL,
	`promotion_price` varchar(100) DEFAULT NULL,
	`priority` int(2) NOT NULL DEFAULT '0',
	`create_time` datetime DEFAULT NULL,
	`last_edit_time` datetime DEFAULT NULL,
	`enable_status` int(2) NOT NULL DEFAULT '0',
	`product_category_id` int(11) DEFAULT NULL,
	`shop_id` int(20) NOT NULL DEFAULT '0',
	PRIMARY KEY (`product_id`),
	KEY `fk_product_procate` (`product_category_id`),
	KEY `fk_product_shop` (`shop_id`),
	CONSTRAINT `fk_product_procate` FOREIGN KEY (`product_category_id`) REFERENCES `tb_product_category` (`product_category_id`),
	CONSTRAINT `fk_product_shop` FOREIGN KEY (`shop_id`) REFERENCES `tb_shop` (`shop_id`)
) ENGINE=InnoDB AUTO_INCREMENT=73 DEFAULT CHARSET=utf8

所以我们定义如下的resultMap:

<mapper namespace="com.yaya.o2o.dao.ProductDao">
    <resultMap id="productMap" type="com.yaya.o2o.entity.Product">
        <id column="product_id" property="productId"/>
        <result column="product_name" property="productName" />
        <result column="product_desc" property="productDesc" />
        <result column="img_addr" property="imgAddr" />
        <result column="normal_price" property="normalPrice" />
        <result column="promotion_price" property="promotionPrice" />
        <result column="priority" property="priority" />
        <result column="create_time" property="createTime" />
        <result column="last_edit_time" property="lastEditTime" />
        <result column="enable_status" property="enableStatus" />
       
        <association property="productCategory" column="product_category_id" javaType="com.yaya.o2o.entity.ProductCategory">
            <id column="product_category_id" property="productCategoryId" />
            <result column="product_category_name" property="productCategoryName"/>
        </association>
        <association property="shop" column="shop_id" javaType="com.yaya.o2o.entity.Shop">
            <id column="shop_id" property="shopId"/>
            <result column="owner_id" property="ownerId"/>
            <result column="shop_name" property="shopName"/>
        </association>
      
        <collection property="productImgList" column="product_id" ofType="com.yaya.o2o.entity.ProductImg">
            <id column="product_img_id" property="productImgId" />
            <result column="detail_img" property="imgAddr" />
            <result column="img_desc" property="imgDesc" />
            <result column="priority" property="priority" />
            <result column="create_time" property="createTime" />
            <result column="product_id" property="productId" />
        </collection>
    </resultMap>
    ......
</mapper>
  • resultMap元素
    • id:对resultMap的唯一标识
    • type:最终映射的java对象类型
  • id元素——查询结果集中唯一标识的定义
    • column:查询出来的列名
    • property:type指定的 pojo中的属性名
  • result元素——对普通列的映射定义
    • column:查询出来的列名
    • property:type指定的 pojo中的属性名
  • association元素——用于映射关联查询单个对象的信息(“有一个”关系)
    • property:映射的属性
    • column:查询的列名
    • javaType:映射的属性的实体类
      • id,column,property同上
  • collection元素——对关联查询到的多条记录映射到集合对象中(“有很多”关系)
    • property:映射的属性
    • column:查询的列名
    • ofType:指定映射到list集合属性中pojo的类型
      • id,column,property同上

然后我们在select中使用resultMap,通过商品id查询商品信息:

<mapper namespace="com.yaya.o2o.dao.ProductDao">
    <select id="queryProductById" resultMap="productMap" parameterType="Long">
        SELECT
            p.product_id,
            p.product_name,
            p.product_desc,
            p.img_addr,
            p.normal_price,
            p.promotion_price,
            p.priority,
            p.create_time,
            p.last_edit_time,
            p.enable_status,
            p.product_category_id,
            p.shop_id,
            pm.product_img_id,
            pm.img_addr AS detail_img,
            pm.img_desc,
            pm.priority,
            pm.create_time
        FROM tb_product p
        LEFT JOIN tb_product_img pm
        ON p.product_id=pm.product_id
        WHERE p.product_id=#{productId}
        ORDER BY pm.priority DESC
    </select>
</mapper>

这样就能完成一对一、一对多的高级映射了~

resultType和resultMap实现一对一查询小结
  • resultType:使用较为简单。如果pojo中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射。
  • resultMap:需要单独定义resultMap,实现麻烦。如果有对查询结果有特殊的要求,使用resultMap可以完成将关联查询映射到pojo的属性中。
  • resultMap可以实现延迟加载,resultType无法实现。

什么是延迟加载?
先从单表查询,需要时再从关联表去关联查询,大大提高数据库的性能,因为查询单表要比关联查询多张表速度要快。association、collection具备延迟加载的功能。

七、动态sql

MyBatis提供对SQL语句动态的组装能力,使用XML的几个简单的元素,便能完成动态SQL的功能。大量的判断都可以在MyBatis的映射XML里面配置,以达到许多需要大量代码才能实现的功能,大大减少了代码量,体现了MyBatis的灵活、高度可配置性和可维护性。

MyBatis的动态SQL包括以下几种元素:

  • if 判断语句
  • choose(when, otherwise) 相当于java中的switch和case
  • trim(when, set) 辅助元素,用于处理特定的SQL拼装问题,比如去掉多余的and,or等
  • foreach 循环语句

下面是项目中用到的if元素和foreach元素的代码实例~

1. if元素

场景:在更新商品信息的时候,只有当传入pojo里属性不为空的,才进行对列名的赋值和拼接

<update id="updateProduct" parameterType="com.yaya.o2o.entity.Product">
	UPDATE tb_product
	<set>
	    <if test="productName != null">product_name=#{productName},</if>
	    <if test="productDesc != null">product_desc=#{productDesc},</if>
	    <if test="imgAddr != null">img_addr=#{imgAddr},</if>
	    <if test="normalPrice != null">normal_price=#{normalPrice},</if>
	    <if test="promotionPrice != null">promotion_price=#{promotionPrice},</if>
	    <if test="priority != null">priority=#{priority},</if>
	    <if test="lastEditTime != null">last_edit_time=#{lastEditTime},</if>
	    <if test="enableStatus != null">enable_status=#{enableStatus},</if>
	    <if test="productCategory != null and productCategory.productCategoryId != null">
	        product_category_id=#{productCategory.productCategoryId}
	    </if>
	</set>
	WHERE product_id=#{productId}
	AND shop_id=#{shop.shopId}
</update>
  • test:在判断语句中,要与if联合使用
2. foreach元素

场景:批量添加商品图片

<insert id="batchInsertProductImg" parameterType="java.util.List">
	INSERT INTO
	     tb_product_img(img_addr, img_desc, priority, create_time, product_id)
	VALUES
	     <foreach collection="list" item="productImg" index="index" separator=",">
	         (
	         #{productImg.imgAddr},
	         #{productImg.imgDesc},
	         #{productImg.priority},
	         #{productImg.createTime},
	         #{productImg.productId}
	         )
	     </foreach>
</insert>
  • collection:传递进来的类型,如数组、List、Set等集合
  • item:循环中当前的元素
  • index:当前元素在集合的位置下标
  • separator:各个元素的间隔符

八、模糊查询

场景:对输入的店铺名称进行模糊查询

...
	<if test="shopCondition.shopName != null">
		and s.shop_name like '%${shopCondition.shopName}%'
	</if>
...
  1. 使用${…}
s.shop_name LIKE '%${shopCondition.shopName}%'

可能会引起sql的注入,尽量避免使用${…}

  1. 使用#{…}
s.shop_name LIKE "%"#{shopCondition.shopName}"%"

因为#{…}解析成sql语句时候,会在变量外侧自动加单引号’ ‘,所以这里 % 需要使用双引号" ",不能使用单引号’ ',否则查不到任何结果。

使用CONCAT()函数进行拼接:
s.shop_name LIKE CONCAT('%', '${shopCondition.shopName}', '%')
---------------------------------------------------------------
s.shop_name LIKE CONCAT('%', #{shopCondition.shopName}, '%')
# 高校智慧校园解决方案摘要 智慧校园解决方案是针对高校信息化建设的核心工程,旨在通过物联网技术实现数字化校园的智能化升级。该方案通过融合计算机技术、网络通信技术、数据库技术和IC卡识别技术,初步实现了校园一卡通系统,进而通过人脸识别技术实现了更精准的校园安全管理、生活管理、教务管理和资源管理。 方案包括多个管理系统:智慧校园管理平台、一卡通卡务管理系统、一卡通人脸库管理平台、智能人脸识别消费管理系统、疫情防控管理系统、人脸识别无感识别管理系统、会议签到管理系统、人脸识别通道管理系统和图书馆对接管理系统。这些系统共同构成了智慧校园的信息化基础,通过统一数据库和操作平台,实现了数据共享和信息一致性。 智能人脸识别消费管理系统通过人脸识别终端,在无需接触的情况下快速完成消费支付过程,提升了校园服务效率。疫情防控管理系统利用热成像测温技术、视频智能分析等手段,实现了对校园人员体温监测和疫情信息实时上报,提高了校园公共卫生事件的预防和控制能力。 会议签到管理系统和人脸识别通道管理系统均基于人脸识别技术,实现了会议的快速签到和图书馆等场所的高效通行管理。与图书馆对接管理系统实现了一卡通系统与图书馆管理系统的无缝集成,提升了图书借阅的便捷性。 总体而言,该智慧校园解决方案通过集成的信息化管理系统,提升了校园管理的智能化水平,优化了校园生活体验,增强了校园安全,并提高了教学和科研的效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值