在上一篇增删改查的基础上继续:
获取自增主键需要数据库支持自增主键
MySQL支持自增主键键值的获取,mybatis也是利用jdbc中statement.getGeneratedKey(),
- useGeneratedKeys=“true”:使用自增主键获取主键值策略
- keyProperty=“id”:指定对应的主键属性:也就是mybatis获取到主键值以后,将这个值封装给javaBean的哪个属性
<!--public int addEmp(Employee e);-->
<!-- parameterType:参数类型,可以省略 -->
<insert id="addEmp" parameterType="bean.Employee"
useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee(last_name,email,gender)
value(#{lastName},#{email},#{gender})
</insert>
一、参数处理:
单个参数:
mybatis不做任何处理,因为只有一个参数,相当于一个占位符。
直接#{参数名}:取出参数的值
多个参数:
会做特殊处理,多个参数会被封装成一个map
- key:param1…paramN,或者参数的索引
- value:传入参数的值
请看下面的操作引发的异常
- mapper接口中:
方法:public Employee getEmpByIdAndLastName(Integer id,String lastName); - mapper.xml中直接拿传入的的参数名取值,但是mybatis不认识。
取值:#{id},#{lastName} - 异常主要信息:
org.apache.ibatis.binding.BindingException:
Parameter ‘id’ not found.
Available parameters are [arg1, arg0, param1, param2]
也印证了上面的结论。
命名参数:
明确指定封装参数时map的key:@Param(“id”),@Param(“lastName”),多个参数会被封装成一个map。
- key:使用@Param注解指定的值
- value:参数值,#{指定的key}即可取出对应的参数值
POJO:
如果多个参数正好是业务逻辑的数据模型,可以直接传入pojo;#{属性名}:取出传入pojo的属性值
Map:
如果多个参数不是数据模型中的数据,没有对应的pojo,为了方便,可以传入map。
public Employee getByMap(Map<String,Object> map);
<!--public Employee getByMap(Map<String,Object> map);-->
<select id="getByMap" resultType="emp">
select * from tbl_employee where id = #{id} and last_name = #{lastName}
</select>
测试
public SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "config\\mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
return new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void test() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("id", 1);
map.put("lastName", "jerry");
Employee e = mapper.getByMap(map);
System.out.println(e);
}finally {
openSession.close();
}
}
如果多个参数不是业务模型中的数据,但是经常要使用,推荐编写DTO(Transfer Object)数据传输对象 ,专门抽取一个对象用于传输。此处不做测试,原理类似POJO。
思考:
public Employee getEmp (@Param("id" )Integer id,String lastName) ;
取值:
- id==>#{id/param1}
- lastName==>#{param2}
public Employee getEmp(Integer id, @Param("e")Employee emp);
取值:
- id==>#{param1}
- lastName===>#{param2.1astName/e.lastName}
特别注意,如果是Collection (List、Set)类型或者是数组,也会特殊处理。也是把传入的list或者数组封装在map中。
- key:
Collection(collection)
List(list)
数组(array) - 示例:查出 id 集合中所有对应的员工
public Employee getEmpById(List<Integer> ids);
取值,取出第一个id的值: #{ids[0]},#{param1[0]}都是错的,正确的写法是#{list[0]}
二、参数取值
id=${id} last_name=#{lastName}
用这两种不同的方式取值,来对比$和#的区别:
select * from tbl_ employee where id=${id} and last_ name=#{lastName}
请看日志打印出来的SQL语句:
Preparing: select * from tbl_ employee where id=2 and last_ name=?
区别:
- #{}:是以预编译的形式,将参数设置到sq1语句中; PreparedStatement;防止sql注入
- ${} :取出的值直接拼装在sql语句中:会有安全问题;
大多情况下,我们去参数的值都应该去使用#{};
好处:原生jdbc不支持占位符的地方我们就可以使用${ }进行取值:
- 比如分表:
按照年份分表拆分,2017_salary,2018_salary这种拼串就很方便
select * from ${year}_ salary where xxx;
- 比如用某个字段排序,升序还是降序
select * from tbl_employee order by ${o_name} ${order};
#{}:更丰富的用法:
规定参数的一些规则:
javaType=#{xxx}、jdbcType. mode (存储过程)、numericScale、
resultMap、typeHandler、 jdbc TypeName、expression (未来准备支持的功能) ;
三、空值处理
jdbcType通常需要在某种特定的条件下被设置:
在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle (报错)
异常为:
JdbcType OTHER: 无效的类型:
原因:
因为mybatis对所有的null都映射的是原生JDBC的OTHER类型
解决办法:
- 解决办法1:取值时指定类型
#{lastName,jdbcType=NULL}
- 解决办法2:全局配置里面settings
<setting name="jdbcTypeForNull" value="NULL"/>