02_Mybatis__SQL映射文件
MyBatis官方文档【中文版】https://mybatis.org/mybatis-3/zh/sqlmap-xml.html
1、增删改查
cache– 该命名空间的缓存配置。cache-ref– 引用其它命名空间的缓存配置。resultMap– 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。parameterMap– 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。sql– 可被其它语句引用的可重用语句块。insert– 映射插入语句。update– 映射更新语句。delete– 映射删除语句。select– 映射查询语句。
EmployeeMapper.java
public void addEmp(Employee employee);
public void updateEmp(Employee employee);
public void deleteEmpById(Integer id);
EmployeeMapper.xml
<!-- public void addEmp(Employee employee); -->
<!--
parameterType:可以省略
-->
<insert id="addEmp" parameterType="com.yuanwu.mybatis.bean.Employee">
insert tbl_employee(last_name,gender,email)
values(#{lastName},#{gender},#{email})
</insert>
<!-- public void updateEmp(Employee employee); -->
<update id="updateEmp">
update tbl_employee
set last_name=#{lastName},gender=#{gender},email=#{email}
where id=#{id}
</update>
<!-- public void deleteEmpById(Integer id); -->
<delete id="deleteEmpById">
delete from tbl_employee where id=#{id}
</delete>
MyBatisTest.java
@Test
public void test03() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//1、获取到的SqlSession不会自动提交数据
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//测试添加
// Employee employee = new Employee(null, "Jerry", "0", "Jerry@yuanwu.com");
// mapper.addEmp(employee);
//测试修改
// Employee employee = new Employee(1, "Back", "1", "Back@yuanwu.com");
// mapper.updateEmp(employee);
//测试删除
mapper.deleteEmpById(1);
//1、手动提交数据
openSession.commit();
} finally {
openSession.close();
}
}
2、insert_获取自增主键的值
<!--
获取自增主键的值:
mybatis支持自增主键,自增主键值的获取,mybatis也是利用 statement.getGeneratedKeys();
useGeneratedKeys="true";使用自增主键获取主键值策略
keyProperty:指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给JavaBean的某个属性
-->
<insert id="addEmp" parameterType="com.yuanwu.mybatis.bean.Employee"
useGeneratedKeys="true" keyProperty="id">
insert tbl_employee(last_name,gender,email)
values(#{lastName},#{gender},#{email})
</insert>
3、insert_获取非自增主键的值
<!--
Oracle不支持自增:Oracle使用序列来模拟自增
每次插入的数据的主键是从序列中拿到的值,如何拿?
-->
<insert id="addEmp" databaseId="oracle">
<!-- keyProperty:查出的主键值封装给JavaBean的哪个属性
order="BEFORE" > 当前sql在插入sql之前运行
AFTER > 当前sql在插入sql之后运行
resultType:查出的数据的返回类型
BEFORE运行顺序:
先运行selectKey 查询id的sql,查出的id值封装给JavaBean的id属性
然后运行插入的sql,就可以取出id属性对应的值
-->
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
<!-- 编写查询主键的sql语句 -->
select EMPLOYEES_SEQ.nextval from dual
</selectKey>
<!-- 插入时的主键是从序列中拿到的 -->
insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL)
values(#{id},#{lastName},#{email})
</insert>
4、参数处理_单个参数&多个参数&命名
EmployeeMapper.java
public Employee getEmpByIdAndLastName(
@Param("id") Integer id,@Param("lastName") String lastName);
EmployeeMapper.xml
<!-- public Employee getEmpByIdAndLastName(Integer id,String lastName); -->
<select id="getEmpByIdAndLastName" resultType="com.yuanwu.mybatis.bean.Employee">
select * from tbl_employee where id = #{id} and last_name=#{lastName}
</select>
MyBatis.java
@Test
public void test04() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//1、获取到的SqlSession不会自动提交数据
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//多个参数查询
Employee employee = mapper.getEmpByIdAndLastName(2, "Jerry");
System.out.println(employee);
} finally {
openSession.close();
}
}
mybatis参数处理.txt 总结
传入单个参数:
#{参数名}:取出参数值
传入多个参数:
多个参数会被封装成一个map,
key:param1, param2...或者参数的索引
value:传入的参数值
#{}就是从map中获取指定的key的值;
绑定异常:
Cause: org.apache.ibatis.binding.BindingException:
Parameter 'id' not found.
Available parameters are [arg1, arg0, param1, param2]
操作:
方法:public Employee getEmpByIdAndLastName(Integer id,String lastName);
取值:id = #{id} and last_name=#{lastName}
命名参数:明确指定封装参数时map的key;@Param("id")
多个参数会被封装成一个map,
key:使用@Param注解指定的值
value:参数值
#{指定的key}取出对应参数值
5、参数处理_POJO&Map&TO
POJO:
如果多个参数正好是我们业务逻辑的数据模型,我们可以直接传入pojo;【传入Javabean对象】
#{属性名},取出传入的pojo的属性值
Map:
如果多个参数不是业务逻辑的数据模型,没有对应的pojo,我们也可以传入map;【不经常使用】
#{key},取出map中对应的值
TO
如果多个参数不是业务逻辑的数据模型,但是要经常使用,推荐编写一个TO(Transfer Object)数据传输对象
EmployeeMapper.xml
<!-- public Employee getEmpByMap(Map<String,Object> map); -->
<select id="getEmpByMap" resultType="com.yuanwu.mybatis.bean.Employee">
select * from tbl_employee where id=#{id} and last_name=#{lastName}
</select>
EmployeeMapper.java
public Employee getEmpByMap(Map<String,Object> map);
MyBatis.java
@Test
public void test04() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//1、获取到的SqlSession不会自动提交数据
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//多个参数查询
// Employee employee = mapper.getEmpByIdAndLastName(2, "Jerry");
Map<String,Object> map = new HashMap<>();
map.put("id", 2);
map.put("lastName","Jerry");
Employee employee = mapper.getEmpByMap(map);
System.out.println(employee);
} finally {
openSession.close();
}
}
6、源码分析_参数封装map的过程
1.MapperProxy > 2.MapperMethod > 3.ParamNameResolver
===================结合源码,mybatis怎么处理参数===============================
ParamNameResolver解析参数封装map
//1、names:{ 0 -> id, 1 -> lastName}:构造器的时候就确定好了
确定流程:
1、获取每个标注了@param注解的参数的@Param的值:id,lastName;赋值给name;【name = ((Param) annotation).value();】
2、每次解析一个参数给map中保存信息:(key:参数索引,value:name的值)
name的值:
标注了param注解:注解的值
没有标注:
1、全局配置:useActualParamName(jdk1.8):name=参数名;【在全局配置中配置了useActualParamName属性】
2、name = String.valueOf(map.size());相当于当前元素的索引
{ 0 -> id, 1 -> lastName,2=2}【如果没标注注解,索引是2,参数也是2】
args[2, "Jerry"]:
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
//1、如果参数为null,直接返回
if (args == null || paramCount == 0) {
return null;
//2、如果只有一个元素,并且没有标注Param注解,返回第一个key, args[0],单个参数
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
//3、多个元素或者标注Param注解
} else {
final Map<String, Object> param = new ParamMap<>();
int i = 0;
//4、遍历names集合:{ 0 -> id, 1 -> lastName,2=2}
for (Map.Entry<Integer, String> entry : names.entrySet()) {
//names集合的value作为key,names集合的key又作为取值的参考args[0]
//最终 ==> eg:{ id=args[0]:1, lastName=args[1]:Jerry, 2=args[2] }
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
//额外的将每一个参数也保存到map中,并使用新的key:param1...paramN
//也就是==> 既可以使用有Param注解的#{指定的key},也可以使用#{param1}
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
}
7、参数处理_#与$取值区别
==============================参数值的获取==============================
#{}:可以获取map中的值或者pojo对象属性的值
${}:可以获取map中的值或者pojo对象属性的值
区别:
#{}:是以预编译的形式,将参数设置到sql语句中,PreparedStatement;防止sql注入
${}:取出的值直接拼装在sql语句中,会有安全问题;
大多数情况下,我们取参数的值都应该使用#{}
表名字段名都不支持预编译,字段值可以
但是,原生jdbc不支持占位符的地方我们就可以使用${}进行取值
比如分表、排序
8、参数处理_#取值时指定参数相关规则
#取值时指定参数相关规则
jdbcType通常需要在某种特定的条件下被设置:
在数据库为null时,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);
因为mybatis对所有的null都映射的是原生jdbc的OTHER类型,Oracle不认识
全局配置中,jdbcTypeForNull=OTHER【默认】,Oracle不支持,有两种方式
1、#{email,jdbcType=OTHER}
2、jdbcTypeForNull=NULL
<setting name="jdbcTypeForNull" value="NULL"/>【全局配置】
9、select_返回List
<!-- public List<Employee> getEmpsByLastNameLike(String lastName);
resultType:如果返回的是一个集合,要写集合中元素的类型
-->
<select id="getEmpsByLastNameLike" resultType="com.yuanwu.mybatis.bean.Employee">
select * from tbl_employee where last_name like #{lastName}
</select>
10、select_记录封装Map
EmployeeMapper.java
//多条记录封装一个map,Map<Integer,Employee>:键是这条记录的主键,值是记录封装后的JavaBean
//告诉mybatis封装这个map的时候使用哪个属性作为map的key
@MapKey("id")
public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName);
//返回一条记录的map,key=列名,值就是对应的值
public Map<String, Object> getEmpByIdReturnMap(Integer id);
EmployeeMapper.xml
<!-- public Map<String, Object> getEmpByIdReturnMap(Integer id); -->
<select id="getEmpByIdReturnMap" resultType="map">
select * from tbl_employee where id=#{id}
</select>
<!-- public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName); -->
<select id="getEmpByLastNameLikeReturnMap" resultType="com.yuanwu.mybatis.bean.Employee">
select * from tbl_employee where last_name like #{lastName}
</select>
MyBatisTest.java
// Map<String, Object> map = mapper.getEmpByIdReturnMap(3);
// System.out.println(map);
Map<Integer, Employee> map = mapper.getEmpByLastNameLikeReturnMap("%r%");
System.out.println(map); //{2=Employee{id=2, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}, 3=Employee{id=3, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}}
11、select_resultMap【自定义结果映射规则】
EMployeeMapperPlus.xml
<?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">
<mapper namespace="com.yuanwu.mybatis.dao.EmployeeMapperPlus">
<!-- 自定义某个JavaBean的封装规则
type:自定义规则的Java类型
id:唯一标识符,方便引用
-->
<resultMap id="MyEmp" type="com.yuanwu.mybatis.bean.Employee">
<!-- 指定主键列的封装规则
id定义主键会底层有优化;
column:指定哪一列
property:指定对应的JavaBean属性
-->
<id column="id" property="id"/>
<!-- result定义普通列封装规则 -->
<result column="last_name" property="lastName"/>
<!-- 其他不指定的列会自动封装,只要写resultMap就把全部的映射规则都写上-->
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</resultMap>
<!-- resultMap:自定义结果集映射规则 -->
<!-- public Employee getEmpById(Integer id);-->
<select id="getEmpById" resultMap="MyEmp">
select * from tbl_employee where id=#{id}
</select>
</mapper>
12、select_resultMap_级联属性
EmployeeMapperPlus.xml
<!--
模拟场景一:
查询Employee的同时查询员工对应的部门
Employee===Department 【一个Employee 对应 一个部门,每一个员工都有部门信息】
一个员工有与之对应的部门信息;
id last_name gender email 【d_id did dept_name【private Department dept;】
-->
<!--
联合查询:级联属性,封装结果集
-->
<resultMap id="MyDifEmp" type="com.yuanwu.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<result column="did" property="dept.id"/>
<result column="dept_name" property="dept.departmentName"/>
</resultMap>
<!-- public Employee getEmpAndDept(Integer id); -->
<select id="getEmpAndDept" resultMap="MyDifEmp">
SELECT e.id id,e.last_name last_name,e.gender gender,e.email email,e.d_id d_id,
d.id did,d.dept_name dept_name
FROM tbl_employee e,tbl_dept d
WHERE e.d_id=d.id AND e.id=#{id}
</select>
测试类
Employee empAndDept = mapper.getEmpAndDept(2);
System.out.println(empAndDept);
System.out.println(empAndDept.getDept());
/*
Employee{id=2, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}
Department{id=1, departmentName='开发部'}
*/
13、select_resultMap_association属性
<!--
使用association标签定义关联单个对象的封装规则;
-->
<resultMap id="MyDifEmp2" type="com.yuanwu.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<!-- association标签可以指定联合的JavaBean对象
property="dept":指定哪个属性是联合对象
javaType:指定这个属性对象的类型【不能省略】
-->
<association property="dept" javaType="com.yuanwu.mybatis.bean.Department">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
</association>
</resultMap>
<!-- public Employee getEmpAndDept(Integer id); -->
<select id="getEmpAndDept" resultMap="MyDifEmp2">
SELECT e.id id,e.last_name last_name,e.gender gender,e.email email,e.d_id d_id,
d.id did,d.dept_name dept_name
FROM tbl_employee e,tbl_dept d
WHERE e.d_id=d.id AND e.id=#{id}
</select>
14、select_resultMap_association分布查询
DepartmentMapper.xml
<!-- public Department getDeptById(Integer id); -->
<select id="getDeptById" resultType="com.yuanwu.mybatis.bean.Department">
select id,dept_name departmentName from tbl_dept where id=#{id}
</select>
EmployeeMapperPlus.xml
<!-- 使用association进行分布查询
1、先按照员工id查询员工信息
2、根据查询员工信息中的d_id值 拿去部门表查出部门信息
3、部门设置到员工中;
-->
<!-- id last_name gender email d_id -->
<resultMap id="MyEmpByStep" type="com.yuanwu.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<!-- association定义关联对象的封装规则
select:表明当前属性是调用select指定方法查出的结果
column:将哪一列的值传给这个方法
流程:
使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
-->
<association property="dept" select="com.yuanwu.mybatis.dao.DepartmentMapper.getDeptById" column="d_id">
</association>
</resultMap>
<!-- public Employee getEmpByIdStep(Integer id); -->
<select id="getEmpByIdStep" resultMap="MyEmpByStep">
select * from tbl_employee where id=#{id}
</select>
测试类
Employee empByIdStep = mapper.getEmpByIdStep(2);
System.out.println(empByIdStep);
System.out.println(empByIdStep.getDept());
/*
Employee{id=2, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}
Department{id=1, departmentName='开发部'}
*/
15、select_resultMap_懒加载
mybatis-config.xml 【在全局配置文件中,设置两个属性】
<!-- 可以使用延迟加载【懒加载】:
Employee==>Dept 【Employee对象包含一个Dept属性】
我们每次查询Employee对象的时候,都将一起查询处理。
我们希望部门信息在我们需要使用的时候再去查询;
分布查询的基础之上加上两个配置;
-->
<!-- 显式的指定需要更改的配置的值,即使它是默认的。-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
测试类
System.out.println(empByIdStep.getLastName());
System.out.println(empByIdStep.getDept());
/*
DEBUG 10-16 21:55:46,405 ==> Preparing: select * from tbl_employee where id=? (BaseJdbcLogger.java:143)
DEBUG 10-16 21:55:46,447 ==> Parameters: 2(Integer) (BaseJdbcLogger.java:143)
DEBUG 10-16 21:55:46,551 <== Total: 1 (BaseJdbcLogger.java:143)
Jerry
DEBUG 10-16 21:55:46,552 ==> Preparing: select id,dept_name departmentName from tbl_dept where id=? (BaseJdbcLogger.java:143)
DEBUG 10-16 21:55:46,553 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:143)
DEBUG 10-16 21:55:46,562 <== Total: 1 (BaseJdbcLogger.java:143)
Department{id=1, departmentName='开发部'}
*/

浙公网安备 33010602011771号