Mybatis使用小记

本文详细介绍了MyBatis框架中的高级技巧,包括参数传递方式、驼峰命名规范配置、扫描包路径设置、联表查询、事务管理及主键返回等核心功能,并提供了具体的代码示例。

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)

@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} 代码第二个参数

public Good selectGood(String id, String name);
   

xml

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

2.2 第二种

通过固定参数的方式
java

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

xml

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

2.3 第三种

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

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

xml

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

3. 开启驼峰命名规范

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

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

	@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. 设置扫描包路径

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

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

@SpringBootApplication
@MapperScan("com.pibigstar.mapper")
public class SeckillApplication{
	
	public static void main(String[] args) {
		SpringApplication.run(SeckillApplication.class, args);
	}
}

5. 联表查询

	/**
	 * 根据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中配置
<!-- 配置事务管理器 -->
<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
	@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默认当方法抛出运行期异常的时候会回滚事务
所以我们一般要将编译期异常转化为运行期异常

@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是我们自定义的运行期异常,主要是处理各种业务异常

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"
<insert id="insert" useGeneratedKeys="true" keyProperty="id" parameterType="com.pibigstar.User">
    insert into user(userName,password,comment)
    values(#{userName},#{password},#{comment})
</insert>
  1. 第二种
<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表示插入之后再进行查询一次

<selectKey resultType="java.lang.Long" order="AFTER" keyProperty="id">
    SELECT LAST_INSERT_ID()
</selectKey>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值