Mybaties开发重点知识1,
不依赖任何ORM框架的话,那么数据库的开发有且只有一个办法,那就是JDBC代码,JDBC三大巨头Connection RepareStatement,ResultSet,是不是此时您已经挂断了电话(您好,您拨打的电话已关机....), JDBC代码比较繁琐,有反射等等,所以出现了所谓的ORM框架,
mysql的话通过 sql mapper来实现了数据对象映射,是半自动化的ORM框架,我们需要自己写SQL语句, mybatie有单个要点操作,首先我们需要去写sql语句查询出数据,然后查出来的数据我们必须要告诉他一个映射规则映射到我们的pojo,第三个是我们的程序dao层方法名与sql语句id的映射规则,(sql,映射规则,pojo),
mybaties对JDBC的Connection,PrepareStatement,ResultSet对象进行了封装增强,就是基于动态代理(method.invoke)进行了代理增强,添加了打印日志的功能与注解事务的功能(动态代理核心两大对象,Proxy,InvocationHaldler),对原有业务的增强,对原有业务没有任何侵入性,(联想到了另外一种设计模式,mabaties打印日志使用了适配器模式,遵循了单一职责原则,一个类就干一种业务,logFactory接口不关心你是log4j还是slf4,你日志模块实现我的适配器接口就可以,我不关心是啥方式打印),
mybaties数据源组件都必须实现javax.sql.DataSource接口,比如德鲁伊,比如c3p0,数据源初始化的参数较多,类较复杂,mybaties用了Factory模式,其实if else加分支违反了单一职责(就干一件事)与开闭(非面向修改)原则(业务逻辑变化不是特别平凡的情况下呢还是可以使用简单工厂模式的),相对的是工厂模式(解决了开闭原则,直接弄自己的工厂,Resource直接注入自己新的工厂,创建复杂对象的时候可以考虑使用工厂模式),对象的创建和对象的使用进行了解耦,类似于Spring的IOC容器,mybatie可以使用连接池获取连接,也可以直接不使用(UnpooledDataSource)连接池方式获取连接,
数据库连接池获取连接详解:
PooledConnection从连接池获取连接,如果是关闭连接,则将连接重新放入连接池中,线程池,
PoolState是对数据库连接池数据结构的封装,两个list一个保存我们活跃的连接,一个保存闲置的连接,还有一些例如请求的次数等属性,此类的toString方法是线程安全的方法,可以把当前对象所有属性通通打出,
获取连接的算法:
归还连接的算法:
获取连接过程简述,getConnection,PooledDataSource里面有一个popConnection方法,作者也考虑到了并发安全线程安全,使用了synchronized加锁(锁对象是我们的连接池对象(对象中有连接池的结构,例如那两个ArrayList)),先判断是否有空闲连接,有空闲连接则获取空闲连接直接使用,并从空闲连接池中将它移除,并将它加入活跃连接池中,没有空闲连接的时候则判断下活跃连接数是不是已经到达了最大值,如果没有达到的话则创建一个连接,创建完之后将它塞到活跃连接池里面来,如果活跃连接数到达了最大值,则检查一下有没有超时的连接(获取最早创建的连接看一下是不是已经超时了getCheckoutTime),如果第一个超时了则将它从我们的活跃连接池中移除(并统计超时信息,例如超时次数,超时时间等,并将这个超时连接中的事务进行回滚,回滚失败则打印错误日志),移除之后则利用这个老的连接创建一个新的连接(只是java对象的创建,对于数据库来说并没有创建新的连接,并且把新对象的创建时间,修改时间等更新成最新的),让超时的老连接(数据库的连接)失效,然后把我们的新连接放入到活跃连接池当中,最后一种分支也是最复杂的分支,如果当前没有空闲连接(而且活跃连接也到达最大值,而且活跃连接全部没有超时),那只能阻塞等待一会继续尝试获取连接,会让连接池累计等待次数加一,调用poolStats的wait方法阻塞当前线程(实际上就是Object的wait方法(释放锁)native方法),获取完之后有一个兜底的判断不等于空则继续执行,wait只是线程协作的初级方法,用不好很容易崩盘,current包有一些countDownLatch,CycliBarrier等高级的线程协作机制类办法,
数据库连接回收的简述:pushConnection,回收的话就没有那么复杂了,同样是sychronized方法,先从活跃连接池中移除此连接,再判断连接是否有效,无效的话那正好,回收方法直接结束,我们一般会设置最大活跃连接数,还会设置最大闲置连接数,判断闲置连接数是不是达到了上线,没有达到上限则进行回收(将此待回收连接的事务进行回滚(如果事务没有提交),基于待回收连接创建一个新连接,重置新连接的创建时间与修改时间,此处只是java对象的创建,创建完后将新连接放入空闲连接池当中,然后让那个老的连接失效,只是让连接对象失效(poolConnection对象中有个失效状态valid置为false,数据库的真实连接还是一直在复用的),并不是让数据库连接失效,失效动作之后唤醒其他被阻塞的线程poolState.notifyAll(因为此时可能那些拿不到连接的线程正在阻塞wait)),然后回收连接方法结束,
在业务执行中,得到连接后还会去尝试pingConnection尝试连接一下数据库,然后再执行业务,
Mybaties的缓存:
mybaties的缓存(一级缓存和二级缓存)是基于Map来实现的,避免我们的请求全部落到数据库,优先从缓存中拿,
多种方法灵活组合的实现方式:
可以用装饰器模式实现,new A1(new A2(new A3(new A))),如果需要组合的方法很少只有一两个,那么用代理模式或者直接写死就可以,如果多了的话那就只能用装饰器模式了,比如IO流就用的装饰器模块,主要是实现了灵活性和扩展性,mybaties的缓存模块就是用的装饰器模式,装饰器模式必然有一个核心接口,装饰器模式是一种用于代替继承的技术,
mybaties缓存对于装饰器的应用:
核心接口catch,其中有一个缓存map对象,有一系列的putObject,getObject,removeObject方法,阻塞式缓存有锁(粗粒度锁和细粒度锁),细粒度锁效率高(分段锁,不同的key有不同的ReetrentLock,存储在ConcurrentHashMap中),第二次会复用第一次查询出的缓存,串行走缓存,mybaties默认是细粒度锁,(锁是当前线程持有的才释放锁),
mybaties的缓存map并不会出现并发安全问题,HashMap的key不是String,而是CacheKey,mybaties也有自带的分页组件,但是我们一般不会去使用,因为他是基于内存分页的,
二级缓存本身会有并发安全问题,但是增加了sychornizedCache装饰器,使线程只能独享,然后一级缓存自然也就没有线程安全问题了,那么二级缓存本身是串行的为什么还是有脏读的情况呢,是以xml的namespace为单元的,A.xml中的缓存更新了,但是B.xml中此数据还没有更新,还是会出现脏读问题,所以二级缓存一般避免使用,
cacheKey的四个元素都相等(还会比较Value集合中的每一个对象都相等),才会认为key是相等的,用指数去计算hashCode,出现碰撞的几率会降低,缓存肯定是在执行sql之前执行的(也就是execute组件), 二级缓存肯定是对JDBC的excute类实现了增强(cacheExecutor),查询的时候先从二级缓存拿,拿到了则直接返回,二级缓存为空,则又去一级缓存中查找(又生成了一次cacheKey),二级缓存中拿到的是cache接口,一级缓存查询结果就是Map中的value,两个都没找到才会丢到数据库中去查询,一级缓存的核心类是BaseExecutor,
开启二级缓存<cache></cache>,这个mapper的查询就会走二级缓存(cacheExecutor),不写这个的话是不会启用走二级缓存的,但是会走一级缓存和数据库,
在查询的时候走了chacheExecutor来封装了key,查询参数也参加了key的封装,
二级缓存中装饰器的体现:
阻塞式缓存的实现:
Mybaties的反射包做的非常精良:
所谓无反射不框架,
平时有搞不定的反射直接拷贝mybaties的源码就行,该第二期mybaties课程 2,
下面4步的实例化对象与对象属性赋值都用到了反射技术,
ObjectFactory接口(实现类DefaultObjectFactory)中最重要的方法,<T> T create(class<T> type), 生成ORM对象,实现类中最终的instantiaeClass方法来实例化对象,暴力反射私有的我也可以访问我说了算,
ReflectorFactory是mybaties反射最核心的类,是对类的元数据的封装,包括类的属性和方法,在项目启动的时候就会加载类的元数据,放到concurrentHashMap中缓存,
mybaties启动pojo加载伪代码:
即使我们的pojo没有写get set方法,mybaties会自动帮我们加上set与get(在addFieds方法生成的),所以说mybaties还是蛮强大的,但是我们一定要自己遵守规范,自己写上,
pojo实例化完之后要进行初始化操作:
使用的ObjectWapper类,结合mateData(ReflectFactory类的可写属性与方法),对(beanWarpper)对象进行属性填充,
MetaObject把mybaties中的反射类全部进行了封装,
我们可以使用MetaObject来完成我们自己业务中的字段反射功能,例如将A对象的数据反射到B对象(就等于是模拟从数据库中读出的数据反射到对象中),下面这段代码也是mybaties查询出数据后大概的执行思路,
以上是对mybaties的核心组件进行的分析,
下面讲解Mybaties的源码流程分析:-
在流程执行到mapper.selectAll()的时候上面讲的那些组件过程就自动执行了,对我们来说是透明的,第一步创建配置对象的时候使用了建造者模式,
.set(0).set(a).set(b).set(c),链式编程,spring scurity就是典型的链式编程,流式编程最大的优点,写代码写起来非常的爽,非常简洁,可读性也非常强,但是调试起来有缺点,SqlSessionFactory就是建造者模式(由于里面的configrution对象过于复杂),
mybaties使用有很多繁杂的配置,造成了这个对象使用建造者模式很合适(BaseBuilder接口),
Mybaties流程解密,-
另一点可以了解的就是,我们的sql通常卸载xml里面,我们也可以用一种更方便的形式,就是把sql卸载我们的mapper类里面,可以搭配一些@select,@delete注解来写,
另一点可以了解的就是,我们可以用@MapperScan来指定扫描的xml的地址(就不需要再mapper接口上面写@Mapper注解了),当然我们也可以用@Mapper注解而不用@MapperSacn注解,
头引入 -->
<?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" >
<!--concat_ws(",",company_address,company_detail_address)-->
子集合查询List的映射:
我们在查询的时候可能查询出的对象含有子列表,那么子列表如何一次性也查出来呢,可以用resultMap来映射子集合,但是要注意的是子集合的字段的名称最好和外层没有重复(可以给个别名),否则会查不出或者只查询出一条, 方法见下面两图(查询用户和用户的角色子列表),
结果map映射-->
<resultMap id="itemPublish" type="itemPublish" >
<id column="id" property="id" jdbcType="BIGINT" />
<result column="platform_id" property="platformId" jdbcType="BIGINT" />
<result column="seller_id" property="sellerId" jdbcType="BIGINT" />
<result column="shop_id" property="shopId" jdbcType="BIGINT" />
<result column="supply_seller_id" property="supplySellerId" jdbcType="BIGINT" />
<result column="supply_shop_id" property="supplyShopId" jdbcType="BIGINT" />
<result column="operator_id" property="operatorId" jdbcType="BIGINT" />
<result column="item_name" property="itemName" jdbcType="VARCHAR" />
<result column="cid" property="cid" jdbcType="BIGINT" />
<result column="second_cid" property="secondCid" jdbcType="BIGINT" />
<result column="brand_id" property="brandId" jdbcType="BIGINT" />
<result column="unit" property="unit" jdbcType="VARCHAR" />
<result column="origin" property="origin" jdbcType="VARCHAR" />
<result column="ad" property="ad" jdbcType="VARCHAR" />
<result column="industry_label" property="industryLabel" jdbcType="VARCHAR" />
<result column="category_attributes" property="categoryAttributes" jdbcType="VARCHAR" />
<result column="spec_attributes" property="specAttributes" jdbcType="VARCHAR" />
<result column="sale_status" property="saleStatus" jdbcType="INTEGER" />
<result column="packing_list" property="packingList" jdbcType="VARCHAR" />
<result column="describe_url" property="describeUrl" jdbcType="VARCHAR" />
<result column="created" property="created" jdbcType="TIMESTAMP"/>
<result column="modified" property="modified" jdbcType="TIMESTAMP" />
<result column="shop_cid" property="shopCid" jdbcType="BIGINT" />
<result column="listing_time" property="listingTime" jdbcType="TIMESTAMP" />
<result column="delisting_time" property="delistingTime" jdbcType="TIMESTAMP"/>
<result column="operator" property="operator" jdbcType="INTEGER" />
<result column="add_source" property="addSource" jdbcType="INTEGER" />
<result column="store_status" property="storeStatus" jdbcType="INTEGER" />
<result column="yn" property="yn" jdbcType="INTEGER" />
<result column="sku_type" property="skuType" jdbcType="VARCHAR" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/> 转换为枚举类型返回
@JsonFormat(shape=Shape.OBJECT)
<result column="attributes" property="attributes" jdbcType="INTEGER" />
<result column="model_code" property="modelCode" jdbcType="INTEGER" />
<result column="bar_code" property="barCode" jdbcType="INTEGER" />
<result column="product_code" property="productCode" jdbcType="INTEGER" />
<result column="sku_status" property="skuStatus" jdbcType="INTEGER" />
<result column="weight" property="weight" jdbcType="INTEGER" />
<result column="picture_url" property="pictureUrl" jdbcType="VARCHAR" />
<result column="alt_images" property="altImages" jdbcType="VARCHAR" />
<result column="sort_number" property="sortNumber" jdbcType="INTEGER" />
<result column="copy_status" property="copyStatus" jdbcType="INTEGER" />
<result column="publishuserId" property="publishuserId" jdbcType="BIGINT" />
<result column="cash_coupon_support" property="cashCouponSupport" jdbcType="INTEGER" />
<result column="meet_coupon_support" property="meetCouponSupport" jdbcType="INTEGER" />
<result column="vip_discount_support" property="vipDiscountSupport" jdbcType="INTEGER" />
<result column="detail_page_qrcode" property="detailPageQrcode" jdbcType="VARCHAR" />
</resultMap>
若resultMap中jdbcType与mysql中有不同 (例如库中数据是tyint类型/对象中是String类型),可以自动转化,,可以set到对象中
标签定义-->
<sql id="Example_Where_Clause" >
<where >
<foreach collection="oredCriteria" item="criteria" separator="or" >
<if test="criteria.valid" >
<trim prefix="(" suffix=")" prefixOverrides="and" >
<foreach collection="criteria.criteria" item="criterion" >
<choose >
<when test="criterion.noValue" >
and ${criterion.condition}
</when>
<when test="criterion.singleValue" >
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue" >
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue" >
and ${criterion.condition}
<foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," >
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
insert语句-->
<insert id="itemPublish" parameterType="item" useGeneratedKeys="true" keyProperty="id">
insert into item (platform_id, seller_id,
shop_id, supply_seller_id, supply_shop_id, operator_id,
item_name, cid,second_cid, brand_id,
unit, origin, ad,
category_attributes, spec_attributes, sale_status,
packing_list, describe_url, created,
modified, shop_cid, listing_time,
delisting_time, operator,
add_source, store_status, yn,copy_status,publishuserId,industry_label,
cash_coupon_support,meet_coupon_support,vip_discount_support,detail_page_qrcode
)
values ( #{platformId,jdbcType=BIGINT}, #{sellerId,jdbcType=BIGINT},
#{shopId,jdbcType=BIGINT}, #{supplySellerId,jdbcType=BIGINT}, #{supplyShopId,jdbcType=BIGINT}, #{operatorId,jdbcType=INTEGER},
#{itemName,jdbcType=VARCHAR}, #{cid,jdbcType=BIGINT},#{secondCid}, #{brandId,jdbcType=BIGINT},
#{unit,jdbcType=VARCHAR}, #{origin,jdbcType=VARCHAR}, #{ad,jdbcType=VARCHAR},
#{categoryAttributes,jdbcType=VARCHAR}, #{specAttributes,jdbcType=VARCHAR}, #{saleStatus,jdbcType=INTEGER},
#{packingList,jdbcType=VARCHAR}, #{describeUrl,jdbcType=VARCHAR}, now(),
now(),#{shopCid,jdbcType=BIGINT},#{listingTime,jdbcType=TIMESTAMP},
#{delistingTime,jdbcType=TIMESTAMP}, #{operator,jdbcType=INTEGER},
#{addSource,jdbcType=INTEGER},#{storeStatus,jdbcType=INTEGER},1,#{copyStatus,jdbcType=INTEGER}, #{publishuserId,jdbcType=BIGINT},#{industryLabel,jdbcType=VARCHAR},
#{cashCouponSupport,jdbcType=INTEGER},#{meetCouponSupport,jdbcType=INTEGER},#{vipDiscountSupport,jdbcType=INTEGER},#{detailPageQrcode,jdbcType=VARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="com....ItemImport" >
insert into item_import_tmp
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="id != null" >
id,
</if>
<if test="platformId != null" >
platform_id,
</if>
<if test="itemName != null" >
item_name,
</if>
<if test="batchNo != null" >
batch_no,
</if>
<if test="state != null" >
state,
</if>
<if test="failNum != null" >
fail_num,
</if>
<if test="failReason != null" >
fail_reason,
</if>
<if test="created != null" >
created,
</if>
<if test="modified != null" >
modified,
</if>
<if test="yn != null" >
yn,
</if>
<if test="itemInfo != null" >
item_info,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="id != null" >
#{id,jdbcType=BIGINT},
</if>
<if test="platformId != null" >
#{platformId,jdbcType=BIGINT},
</if>
<if test="itemName != null" >
#{itemName,jdbcType=VARCHAR},
</if>
<if test="batchNo != null" >
#{batchNo,jdbcType=VARCHAR},
</if>
<if test="state != null" >
#{state,jdbcType=INTEGER},
</if>
<if test="failNum != null" >
#{failNum,jdbcType=INTEGER},
</if>
<if test="failReason != null" >
#{failReason,jdbcType=VARCHAR},
</if>
<if test="created != null" >
#{created,jdbcType=TIMESTAMP},
</if>
<if test="modified != null" >
#{modified,jdbcType=TIMESTAMP},
</if>
<if test="yn != null" >
#{yn,jdbcType=INTEGER},
</if>
<if test="itemInfo != null" >
#{itemInfo,jdbcType=LONGVARCHAR},
</if>
</trim>
</insert>
<!-- 批量添加item_picture信息 -->
<insert id="insertItemPictureList" parameterType="java.util.List">
insert into item_picture( platform_id, item_id, shop_id, seller_id, picture_url,alt_images, sort_number, created, modified, yn)
values
<foreach collection="list" item="itemPicture" index="index" separator=",">
(#{itemPicture.platformId,jdbcType=BIGINT},
#{itemPicture.itemId,jdbcType=BIGINT},
#{itemPicture.shopId,jdbcType=BIGINT},
#{itemPicture.sellerId,jdbcType=BIGINT},
#{itemPicture.pictureUrl,jdbcType=VARCHAR},
#{itemPicture.altImages,jdbcType=VARCHAR},
#{itemPicture.sortNumber,jdbcType=INTEGER},
now(),now(), 1)
</foreach>
</insert>
删除语句-->
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
delete from item_import_tmp
where id = #{id,jdbcType=BIGINT}
</delete>
<delete id="deleteByExample" parameterType="com.jd.ecc.b2b.item.domain.item.domain.ItemImportExample" >
delete from item_import_tmp
<if test="_parameter != null" >
<include refid="Example_Where_Clause" />
</if>
</delete>
修改语句-->
<update id="updateSaleStatus" parameterType="map">
UPDATE item
<set>
store_status = #{storeStatus},
<if test="saleStatus != null">
sale_status = #{saleStatus}
</if>
</set>
WHERE id = #{itemId}
AND platform_id = #{platformId}
</update>
<update id="updateItem" parameterType="itemPublish">
update item set
operator_id=#{operatorId,jdbcType=INTEGER},item_name=#{itemName,jdbcType=VARCHAR},cid=#{cid,jdbcType=BIGINT},second_cid=#{secondCid},brand_id=#{brandId,jdbcType=BIGINT},
unit=#{unit,jdbcType=VARCHAR},origin=#{origin,jdbcType=VARCHAR},ad=#{ad,jdbcType=VARCHAR},category_attributes=#{categoryAttributes,jdbcType=VARCHAR},
spec_attributes=#{specAttributes,jdbcType=VARCHAR},sale_status=#{saleStatus},store_status = #{storeStatus},
packing_list=#{packingList,jdbcType=VARCHAR},describe_url=#{describeUrl,jdbcType=VARCHAR},shop_cid=#{shopCid,jdbcType=BIGINT},
operator=#{operator,jdbcType=INTEGER},industry_label=#{industryLabel},
cash_coupon_support=#{cashCouponSupport},meet_coupon_support=#{meetCouponSupport},vip_discount_support=#{vipDiscountSupport}
<if test="publishuserId != null">
,publishuserId=#{publishuserId}
</if>
where id=#{id,jdbcType=BIGINT} and platform_id=#{platformId} and yn=1
</update>
查询语句 -->
<select id="queryItems" parameterType="sellSupplyItemInfoVo" resultMap="SellSupplyItemMap">
select DISTINCT i.id itemId,i.platform_id, i.item_name, sii.supply_seller_id, sii.supply_shop_id,ip.picture_url,
category.cname1, category.cname2, category.cname3, category.cname4
from shop_item_library sii
JOIN item i
on sii.item_id=i.id
and i.store_status = 30
and sii.supply_status = 0
JOIN item_picture ip
on i.id = ip.item_id
LEFT JOIN item_sku isk
on i.id = isk.item_id
LEFT JOIN item_brand ib
on i.brand_id = ib.id
JOIN
(
SELECT c4.c_name cname1,c3.c_name cname2,c2.c_name cname3,c1.c_name cname4,c1.cid cid
FROM item_category c1
LEFT JOIN item_category c2 ON c1.parent_cid = c2.cid
LEFT JOIN item_category c3 ON c2.parent_cid = c3.cid
LEFT JOIN item_category c4 ON c3.parent_cid = c4.cid
WHERE
c1.platform_id = #{platformId}
<if test="cid != null and cid != ''">
And c1.cid= #{cid}
</if>
) category ON i.cid = category.cid
where sii.supply_seller_id = #{supplySellerId}
AND i.platform_id = #{platformId}
<if test="productCode != null and productCode != ''">
AND isk.product_code LIKE CONCAT('%', #{productCode},'%' )
</if>
<if test="modelCode != null and modelCode != ''">
AND isk.model_code LIKE CONCAT('%', #{modelCode},'%' )
</if>
<if test="itemName != null and itemName != ''">
AND i.item_name LIKE CONCAT('%', #{itemName},'%' )
</if>
</select>
<select id="selectByExample" resultMap="BaseResultMap" parameterType="com....ItemImportExample" >
select
<if test="distinct" >
distinct
</if>
'true' as QUERYID,
<include refid="Base_Column_List" />
from item_import_tmp
<if test="_parameter != null" >
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null" >
order by ${orderByClause}
</if>
</select>
时间判断
<if test="endTime != null">
AND up.audit_time < DATE_ADD(#{endTime}, INTERVAL 1 DAY)
</if>
一次更新多条:
<update id="batchUpdate" parameterType="list">
update item_category
<trim prefix="set" suffixOverrides=",">
<trim prefix="cate_code_path =case" suffix="end,">
<foreach collection="list" item="i" index="index">
<if test="i.cateCodePath!=null">
when (cid = #{i.cid} and platform_id = #{i.platformId}) then #{i.cateCodePath}
</if>
</foreach>
</trim>
<trim prefix="cate_name_path =case" suffix="end," >
<foreach collection="list" item="i" index="index">
<if test="i.cateNamePath!=null">
when (cid = #{i.cid} and platform_id = #{i.platformId}) then #{i.cateNamePath}
</if>
</foreach>
</trim>
</trim>
where
<foreach collection="list" separator="or" item="i" index="index">
(cid = #{i.cid} and platform_id = #{i.platformId})
</foreach>
</update>
MapperSQL常见问题
1、我们在写sql的时候,在WHERE 条件中会写这样的语句:
<if test="wmHasRead != null and wmHasRead !=''"><![CDATA[AND wm_has_read = #{wmHasRead}]]></if>
此时要注意wmHasRead 这个字段是Integer类型,我们用了<if test="wmHasRead != null and wmHasRead !=''">来判断是否执行此条件,那么这样即使这个字段你传了正确的值也不会执行,去掉标黄色部分就好了,应为mybatis底层会检测此字段的类型,类型不予数据库中相符会自动跳过这个字段。
2、SELECT E_Name FROM Employees_China
UNION ALL
SELECT E_Name FROM Employees_USA
默认地,UNION 操作符选取不同的值。如果允许重复的值,请使用 UNION ALL。
3、DISTINCT用法,跟在select 后方,去除重复的行
4、有时候我们XML凭借的SQL过于复杂,调试bug的时候如果想打印sql的话SpringBoot用如下配置,标红为dao接口路径
logging.level.com.csdn.bspo.system.dao.mapper=debug
5、DATE_FORMAT(WARRANTY_END_TIME,'%Y-%m-%d %H:%i:%S')函数可以吧sql中的时间格式化,通常用来查询,但是如果将它用来进行时间范围查询的话边界会查询不出,and DATE_FORMAT(WARRANTY_END_TIME,'%Y-%m-%d %H:%i:%S') = "2019-11-14 00:00:00",此处即时你的数据库timestamp时间是2019-11-14 00:00:00也查询不出,可以用去掉函数直接比较的方式
最后说一下CDATA 区段的用法:
<select id="queryItemCategoryAttrIds" resultType="java.lang.String">
<![CDATA[
SELECT concat(id,'-',uuid) as uuid
FROM item_category_attr_import_tmp icit
WHERE icit.platform_id = #{platformId} AND icit.yn = 1 AND icit.status in(10,40)
and icit.try_num < 5
]]>
ORDER BY try_num
limit 500
</select>
假如您在 XML 文档中放置了一个类似 "<" 字符,那么这个文档会产生一个错误,在 XML 中有 5 个预定义的实体引用:
< | < | 小于 |
> | > | 大于 |
& | & | 和号 |
' | ' | 省略号 |
" | " | 引号 |
在 XML 元素中,"<" 和 "&" 是非法的。
"<" 会产生错误,因为解析器会把该字符解释为新元素的开始。
"&" 也会产生错误,因为解析器会把该字符解释为字符实体的开始。
关于 CDATA 部分的注释:
CDATA 部分不能包含字符串 "]]>"。也不允许嵌套的 CDATA 部分。
标记 CDATA 部分结尾的 "]]>" 不能包含空格或折行。
注: where 1=1 在特殊的环境会报错,如果我们有特殊需要的话不能写,
<insert id="itemPublish" parameterType="item" useGeneratedKeys="true" keyProperty="id"> 主键返回,
也有insert多条,返回insert条数的写法
推荐1:自动寻找DAOmapper方法对应XMLsql的插件:myBatisCodeHelperPro插件,mapper中击小鸟直接飞到对应的xml, 自动创建xml, 如果没有对应的xml会报红, alt+Enter会自动生成对应的xml-不过SQL需要自己写。。
推荐2:mybatis热部署 插件 JRebel
和IDEA自带的tomcat热部署区别: 本身默认的只能进行方法修改的热部署,及静态资源文件的热部署
意味着:一个Module的开发除了日常的开关机、切换Module,从开始到结束 只需要 Ctrl + f9
Jrebal需要破解 http://blog.youkuaiyun.com/lsgqjh/article/details/52848172
另外mybaties也可以一次性执行多条语句:
允许一次性执行多条语句需要添加一些配置如下:
allowmutiqueries=true; 此外xml中多条sql之间加上分号就行,事务需要试验一下,