Mybatis使用小记

@[toc]

1. ${} 和 #{}的区别

Mybatis中的#{}用于传递查询的参数,用于从dao层传递一个string参数过来(也可以是其他参数),select * from 表名 order by age=#{age}

Mybatis会把这个参数转换成一个字符串。select * from 表名 order by age=”age” 相当于jdbc中的预编译,安全。
${}一般用于order by的后面,Mybatis不会对这个参数进行任何的处理,直接生成了sql语句。例:传入一个年龄age的参数,select * from 表名 order by ${age}

所以首选使用 #{}

但是传递参数为一个以上的时候,需要我们用注解去绑定参数
通过使用 @Param注解的方式(org.apache.ibatis.annotations.Param)

1
2
@Select("select * from seckill order by create_time desc limit #{offset},#{limit}")
List<Seckill> queryAll(@Param("offset")int offset,@Param("limit")int limit);

2. 传递参数

2.1 第一种

通过#{0},#{1} 方式,#{0} 代码第一个参数,#{1} 代码第二个参数

1
public Good selectGood(String id, String name);

xml

1
2
3
<select id="selectGood" resultMap="GoodMap">
select * from good where id = #{0} and name=#{1}
</select>

2.2 第二种

通过固定参数的方式
java

1
public Good selectGood(@param("id")String id,@param("name")String name);

xml

1
2
3
<select id="selectGood" resultMap="GoodMap">
select * from good where id = #{id} and name=#{name}
</select>

2.3 第三种

通过Map集合去传递(推荐)
java

1
2
3
4
5
6
7
public Good selectGood(){
Map map = new HashMap();
map.put("id",1);
map.put("name","pibigstar");
Good good = goodService.selectGood(map);
return good;
}

xml

1
2
3
<select id="selectGood" resultMap="GoodMap">
select * from good where id = #{id} and name=#{name}
</select>

3. 开启驼峰命名规范

1
2
//在application.properties中设置
mybatis.configuration.map-underscore-to-camel-case=true //开启驼峰命名

如果不设置需要手动在查询的时候设置:

1
2
3
4
5
6
7
8
9
@Select("select * from seckill")
@Results({
//如果不开启驼峰命名,就要手动这样设置
@Result(property="seckillId",column="seckill_id"),
@Result(property="createTime",column="create_time"),
@Result(property="startTime",column="start_time"),
@Result(property="endTime",column="end_time"),
})
List<Seckill> list();

4. 设置扫描包路径

1
2
//application.properties中设置
mybatis.type-aliases-package=com.pibigstar.domain

如果不设置可以在启动类中手动设置

1
2
3
4
5
6
7
8
@SpringBootApplication
@MapperScan("com.pibigstar.mapper")
public class SeckillApplication{

public static void main(String[] args) {
SpringApplication.run(SeckillApplication.class, args);
}
}

5. 联表查询

1
2
3
4
5
6
7
8
9
10
	/**
* 根据id拿到SuccessSeckilled并携带秒杀商品的对象
* @param seckillId
* @return
*/
@Select("select sk.seckill_id,sk.user_phone,sk.create_time,sk.state,"
+ " s.seckill_id 'seckill.seckill_id',s.name 'seckill.name',s.number 'seckill.number',s.create_time 'seckill.create_time',s.start_time 'seckill.start_time',s.end_time 'seckill.end_time'"
+ " from success_killed sk inner join seckill s on sk.seckill_id=s.seckill_id "
+ " where sk.seckill_id=#{seckillId} and sk.user_phone=#{userPhone}")
SuccessKilled queryByIdWithSeckill(@Param("seckillId")long seckillId,@Param("userPhone")long userPhone);

6. 事务

事务方法的执行时间要尽可能短,不要穿插一些网络操作(RPC/HTTP)请求,如果必须需要,那么将这些网络操作剥离出来(定义一个网络操作方法,将拿到的数据再传递给此事务方法)

不是所有的方法都需要事务,比如: 只有一条修改操作,或只有 只读操作
一般都是读操作和写操作要在同一个方法中时需要申明事务

6.1 添加事务

  • 在xml中配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>

<!-- 配置事务传播特性 -->
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置参与事务的类 -->
<aop:config>
<aop:pointcut id="allTestServiceMethod" expression="execution(* com.test.testAda.test.model.service.*.*(..))"/>
<aop:advisor pointcut-ref="allTestServiceMethod" advice-ref="TestAdvice" />
</aop:config>
  • 在方法中添加注解@Transactional
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    @Transactional
    public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
    throws SeckillException, RepeatkillException, SeckillCloseException {

    if (md5==null||!md5.equals(MD5Util.getMD5(seckillId))) {
    throw new SeckillException("MD5值不合法");
    }
    try {
    //执行秒杀,减少库存
    Date nowTime = new Date();
    int updateCount = seckillMapper.reduceNumber(seckillId, nowTime);
    if (updateCount<=0) {
    //没有更新到记录,秒杀结束
    throw new SeckillCloseException("秒杀结束");
    }else {
    //记录购买行为
    int insertCount = successKilledMapper.insert(seckillId, userPhone);
    if (insertCount<=0) {
    //重复插入(主键为seckill和userphone联合主键)
    //返回0,重复秒杀
    throw new RepeatkillException("重复秒杀");
    }else {
    //返回1,秒杀成功
    SuccessKilled successKilled = successKilledMapper.queryByIdWithSeckill(seckillId, userPhone);
    return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS,successKilled);
    }

    }
    }catch (SeckillCloseException e1) {
    throw e1;
    }catch (RepeatkillException e2) {
    throw e2;
    }
    catch (Exception e) {
    logger.error(e.getMessage(),e);
    //所有编译期异常 转换为了运行期异常
    throw new SeckillException("seckill inner error:"+e.getMessage());
    }

    }

6.2 事务回滚

spring默认当方法抛出运行期异常的时候会回滚事务
所以我们一般要将编译期异常转化为运行期异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
@Transactional
public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
throws SeckillException, RepeatkillException, SeckillCloseException {

if (md5==null||!md5.equals(MD5Util.getMD5(seckillId))) {
throw new SeckillException("MD5值不合法");
}
try {
//执行秒杀,减少库存
Date nowTime = new Date();
int updateCount = seckillMapper.reduceNumber(seckillId, nowTime);
}
catch (Exception e) {
logger.error(e.getMessage(),e);
//将所有编译期异常 转换为了运行期异常
throw new SeckillException("seckill inner error:"+e.getMessage());
}

SeckillException是我们自定义的运行期异常,主要是处理各种业务异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.pibigstar.exception;

/**
* 秒杀相关业务异常
* @author pibigstar
*/
public class SeckillException extends RuntimeException{
private static final long serialVersionUID = 1L;

public SeckillException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public SeckillException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}

7. 插入返回主键

  1. 第一种:
    加入这两个属性:useGeneratedKeys="true" keyProperty="id"

    1
    2
    3
    4
    <insert id="insert" useGeneratedKeys="true" keyProperty="id" parameterType="com.pibigstar.User">
    insert into user(userName,password,comment)
    values(#{userName},#{password},#{comment})
    </insert>
  2. 第二种

    1
    2
    3
    4
    5
    6
    7
    <insert id="insertProduct" parameterType="com.pibigstar.model.User" >
    <selectKey resultType="java.lang.Long" order="AFTER" keyProperty="id">
    SELECT LAST_INSERT_ID()
    </selectKey>
    insert into user(userName,password,comment)
    values(#{userName},#{password},#{comment})
    </insert>

order为AFTER表示插入之后再进行查询一次

1
2
3
<selectKey resultType="java.lang.Long" order="AFTER" keyProperty="id">
SELECT LAST_INSERT_ID()
</selectKey>
-------------本文结束感谢您的阅读-------------