mybatis-sql映射文件
一、增删改查
- mybatis允许增删改的方法直接定义返回值为Integer、Long、Boolean、void这些类型的,mybatis会自动帮我们封装返回值
- sqlSessionFactory.openSession(),获取到的SqlSession不会自动提交数据,我们需要手动提交数据;sqlSessionFactory.openSession(true);===》自动提交
映射插入语句
-
自增主键添加
mysql支持自增主键 useGeneratedKeys="true";使用自增主键获取主键值策略 keyProperty="id";指定对应的主键属性;
mybatis利用statement.getGenreatedKeys()获取到自增主键值后,将这个值封装给javaBean的哪个属性;不定义的话对象是获取不到id值的
<insert id="addEmp" parameterType="com.mybatis.bean.Employee" useGeneratedKeys="true" keyProperty="id" databaseId="mysql"> insert into tbl_employee(last_name,email,gender) values(#{lastName},#{email},#{gender}) </insert>
-
非自增主键添加
Oracle不支持自增,Oracle使用序列来模拟自增,每次插入的数据的主键是从序列中拿到的值;
keyProperty:查出的主键值封装给javaBean的哪个属性
order="BEFORE":先运行selectKey查询id的sql;查出id值封装给javaBean的id属性
resultType:查出的数据的返回值类型
<insert id="addEmp" databaseId="oracle">
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
select EMPLOYEES_SEQ.nextval from dual
</selectKey>
insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL)
values(#{id},#{lastName},#{email<!-- ,jdbcType=NULL -->})
</insert>
order="AFTER":先运行插入的sql(从序列中取出新值作为id);再运行selectKey查询id的sql;
ps:如果同时插入多条数据,这样获取到的序列值不是当前插入的id
<insert id="addEmp" databaseId="oracle"> <selectKey keyProperty="id" order="AFTER" resultType="Integer"> select EMPLOYEES_SEQ.currval from dual </selectKey> insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) values(employees_seq.nextval,#{lastName},#{email}) </insert>
映射删除语句
<delete id="deleteEmpById"> delete from tbl_employee where id=#{id} </delete>
映射更新语句
<update id="updateEmp"> update tbl_employee set last_name=#{lastName},email=#{email},gender=#{gender} where id=#{id} </update>
映射查询语句
<select id="getEmpById" resultType="com.mybatis.bean.Employee"> select * from tbl_employee where id = #{id} </select>
代码
@Test public void test03() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); //获取到的SqlSession不会自动提交数据 SqlSession openSession = sqlSessionFactory.openSession(); try{ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); //测试添加 Employee employee = new Employee(null, "jerry4",null, "1"); mapper.addEmp(employee); System.out.println(employee.getId()); //测试修改 //Employee employee = new Employee(1, "Tom", "jerry@com", "0"); //boolean updateEmp = mapper.updateEmp(employee); //System.out.println(updateEmp); //测试删除 //mapper.deleteEmpById(2); //手动提交数据 openSession.commit(); }finally{ openSession.close(); }
二、参数处理
- sql映射文件中的参数类型parameterType可以省略,mybatis会根据TypeHandle自动判断;个人认为如果写入的话能让开发者更清楚对应的是哪个JavaBean类或者哪种数据结构。
- returnType定义别名或全类名
mybatis传入参数
1、单个参数:mybatis不会做特殊处理,#{参数名/任意名}:取出参数值。
2、多个参数:mybatis会做特殊处理。多个参数会被封装成 一个map,mybatis会帮我们把多个参数的key定义成param1...paramN
key:param1...paramN,或者参数的索引也可以
value:传入的参数值
#{}就是从map中获取指定的key的值;
如果我们按照接口方法传入的参数名称去获取会出现以下异常
操作: 方法:public Employee getEmpByIdAndLastName(Integer id,String lastName); 取值:#{id},#{lastName} 异常: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [1, 0, param1, param2]
解决方法有2种:
- #{param1},#{param2}取值即可,但这种方式不能见名知意
- 命名参数:明确指定封装参数时map的key;在方法参数前加上@Param("参数名"),这样key就会使用@Param注解指定的值
public Employee getEmpByIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName);
3、POJO:如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;#{属性名}:取出传入的pojo的属性值
4、Map:如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map;#{key}:取出map中对应的值
5、TO:如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象
6、如果参数类型是Collection(List、Set)或者数组也会特殊处理。把传入的list或者数组封装在map中,
key命名:
Collection——(collection)
List——(list)
数组——(array)
取值方式
#{list[0]}
mybatis获取参数值
#{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
${}:取出的值直接拼装在sql语句中;会有安全问题;
- 大多情况下,我们取参数的值都应该去使用#{};
- 原生jdbc不支持占位符的地方我们就可以使用${}进行取值
#{}更多用法
参数的一些规则:javaType、 jdbcType、 mode(存储过程)、 numericScale、resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能)
ps:在我们传入数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);这是因为mybatis在全局配置中对所有的null映射的是原生Jdbc的OTHER类型,而oracle不能识别JdbcType OTHER(无效的类型):
两种解决方案
1、#{email,jdbcType=OTHER};
2、修改全局配置jdbcTypeForNull属性的值<setting name="jdbcTypeForNull" value="NULL"/>
${}使用场景
比如分表、排序。。。;按照年份分表拆分 select * from ${year}_salary where xxx; select * from tbl_employee order by ${f_name} ${order}
mybatis返回结果集
-
resultType指定期望结果集的全类名或别名,如果数据模型与数据库字段名称不匹配,可以开启驼峰命名或者在sql写别名的方式对应起来
以简单数据结构返回的这里就省略了;这里主要说下list、map以及指定相应泛型的方法。
如果返回的是一个list,要写集合中元素的类型,而不是list(resultType="javaBean的全类名/别名");我们想要的就是javaBean,只是结果有多条,mybatis会帮我们自动写入到一个list返回给我们
如果返回的是一条记录的map;key就是列名,值就是对应的值(resultType="map")
如果是多条记录封装一个map,比如Map<Integer,Employee>:键是这条记录的主键,值是记录封装后的javaBean,我们可以通过@MapKey告诉mybatis封装这个map的时候使用哪个属性作为map的key;而它的resultType="javaBean的全类名/别名"
@MapKey("id") public Map<String, Employee> getEmpByLastNameLikeReturnMap(String lastName);
总结:returnType始终写的是我们想要mybatis把每一条记录封装成什么类型,无论是list还是多条记录的map,我们真正想要的是JavaBean的数据模型,mybatis会帮我们把这些数据封装成我们定义的方法返回类型
-
resultMap:自定义某个javaBean的封装规则
type:自定义规则的java类型
id:唯一id方便引用
1、简单查询
<resultMap type="com.mybatis.bean.Employee" id="MySimpleEmp"> <!--指定主键列的封装规则 id定义主键会底层有优化; column:指定哪一列 property:指定对应的javaBean属性 --> <id column="id" property="id"/> <!-- 定义普通列封装规则 --> <result column="last_name" property="lastName"/> <!-- 其他不指定的列会自动封装:我们只要写resultMap就把全部的映射规则都写上。 --> <result column="email" property="email"/> <result column="gender" property="gender"/> </resultMap>
<select id="getEmpById" resultMap="MySimpleEmp"> select * from tbl_employee where id=#{id} </select>
2、联合查询(封装单对象)
-
嵌套结果集的方式,级联属性封装结果集
<resultMap type="com.mybatis.bean.Employee" id="MyDifEmp"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="gender" property="gender"/> <result column="did" property="dept.id"/> <result column="dept_name" property="dept.departmentName"/> </resultMap>
-
嵌套结果集的方式,使用association定义关联的单个对象的封装规则
<resultMap type="com.mybatis.bean.Employee" id="MyDifEmp2"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="gender" property="gender"/> <!-- association可以指定联合的javaBean对象 property="dept":指定哪个属性是联合的对象 javaType:指定这个属性对象的类型[不能省略] --> <association property="dept" javaType="com.mybatis.bean.Department"> <id column="did" property="id"/> <result column="dept_name" property="departmentName"/> </association> </resultMap> <select id="getEmpAndDept" resultMap="MyDifEmp"> SELECT e.id id,e.last_name last_name,e.gender gender,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>
<select id="getEmpAndDept" resultMap="MyDifEmp"> SELECT e.id id,e.last_name last_name,e.gender gender,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>
-
使用association进行分步查询
<resultMap type="com.mybatis.bean.Employee" id="MyEmpByStep"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="email" property="email"/> <result column="gender" property="gender"/> <!-- association定义关联对象的封装规则 select:表明当前属性是调用select指定的方法查出的结果(namespace+标识id) column:指定将哪一列的值传给这个方法作为传参的参数 --> <association property="dept" select="com.mybatis.dao.DepartmentMapper.getDeptById" column="d_id"> </association> </resultMap>
//引用MyEmpByStep,它会根据加载策略进行二次查询
<select id="getEmpByIdStep" resultMap="MyEmpByStep"> select * from tbl_employee where id=#{id} </select>
//dept部分 <select id="getDeptById" resultType="com.mybatis.bean.Department"> select id,dept_name departmentName from tbl_dept where id=#{id} </select>
3、联合查询(封装Collection)
- 嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则
<resultMap type="com.mybatis.bean.Department" id="MyDept"> <id column="did" property="id"/> <result column="dept_name" property="departmentName"/> <!-- collection定义关联集合类型的属性的封装规则 ofType:指定集合里面元素的类型 --> <collection property="emps" ofType="com.mybatis.bean.Employee"> <!-- 定义这个集合中元素的封装规则 --> <id column="eid" property="id"/> <result column="last_name" property="lastName"/> <result column="email" property="email"/> <result column="gender" property="gender"/> </collection> </resultMap>
<select id="getDeptByIdPlus" resultMap="MyDept"> SELECT d.id did,d.dept_name dept_name, e.id eid,e.last_name last_name,e.email email,e.gender gender FROM tbl_dept d LEFT JOIN tbl_employee e ON d.id=e.d_id WHERE d.id=#{id} </select>
-
使用collection进行分段查询
<resultMap type="com.mybatis.bean.Department" id="MyDeptStep"> <id column="id" property="id"/> <id column="dept_name" property="departmentName"/> <collection property="emps" select="com.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId" column="id"></collection>
//如果需要多个查询条件的话,使用map进行传递column="{key1=column1,key2=column2}",其中key1是getEmpsByDeptId获取的参数名,column1是我们传递过去的属性值{deptId=id}
</resultMap>
<select id="getDeptByIdStep" resultMap="MyDeptStep">
select id,dept_name from tbl_dept where id=#{id}
</select>
鉴别器
可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
<resultMap type="com.mybatis.bean.Employee" id="MyEmpDis"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="email" property="email"/> <result column="gender" property="gender"/> <!-- column:指定判定的列名;javaType:列值对应的java类型 --> <discriminator javaType="string" column="gender"> <!-- 如果=0,查询出dept的信息 --> <case value="0" resultType="com.mybatis.bean.Employee"> <association property="dept" select="com.mybatis.dao.DepartmentMapper.getDeptById" column="d_id"> </association> </case> <!-- 如果=1,把last_name这一列的值赋值给email --> <case value="1" resultType="com.mybatis.bean.Employee"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="last_name" property="email"/> <result column="gender" property="gender"/> </case> </discriminator> </resultMap>
延迟加载(按需加载)
- 使用分步查询可以使用全局配置文件制定延迟加载策略
<!--显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题 --> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/>
-
也可在collection或association里加入fetchType
fetchType="lazy" - lazy:延迟 - eager:立即