MyBatis
1.3 常见问题及解决
1.3.1 解决数据库字段名与JavaBean属性名不匹配问题:
resultMap – The most complicated and powerful element that describes how to load your objects from the database resultsets. (描述怎么把数据库中的内容加载到对象中去)
数据库的字段名与Java类的属性名名称不匹配了,并且双方都不能修改,这时怎么办?
问题:
Java类的属性:
数据库的字段名:
<?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.home.java.AdminMapper">
<select id="queryAllAdmin" resultType="com.home.java.Admin">
select * from tb_admin
</select>
</mapper>
public class Main {
public static void main(String[] args) throws IOException {
String res = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(res);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = sqlSessionFactory.openSession();
AdminMapper mapper = session.getMapper(AdminMapper.class);
List<Admin> admins = mapper.queryAllAdmin();
System.out.println(admins.get(0).getAccount());//null 因为在数据库中找不到account的字段
}
}
解决问题
在xml文件中添加resultMap
<?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.home.java.AdminMapper">
<resultMap id="zaq" type="com.home.java.Admin">
<id property="id" column="id"></id> <!--property表示对象的属性名,column表示数据库中的字段名-->
<result property="account" column="username"></result>
<result property="pwd" column="pwd"></result>
<!--javaType表示在Java中property的类型是什么,jdbcType同理-->
</resultMap>
<select id="queryAllAdmin" resultMap="zaq">
select * from tb_admin
</select>
</mapper>
这样不用修改名称就解决了不匹配的问题,resultMap就相当于是数据库与Java代码之间的桥梁!
<resultMap>:自定义映射,处理复杂的表的关系
-
<id>:设置主键的映射关系,column设置字段名,property设置属性名
-
<result>:设置非主键的映射关系,column设置字段名,property设置属性名
resultMap下的id标签就是专门用来设置主键的映射关系,且只能有一个
1.3.2 实现添加时获取自动生成的主键
<insert id="saveDepartment" parameterType="com.home.java.bean.Department" useGeneratedKeys="true" keyProperty="id" >/*为了能在插入过程中拿到id值,在此设置这两个属性*/
insert into tb_dep(depname) values (#{name})
</insert>
keyProperty:表示将生成的主键赋值给传入参数的哪个属性!
比如上面的过程,解读:传过来了一个对象(department的对象),然后把insert执行完生成的主键赋值给这个对象的id属性!
总结:
- useGeneratedKeys:可以使用自动生成的主键
- keyProperty:将自动生成的主键赋值给传递过来的参数的对应属性,即Mybatis获取主键后,将这个值封装给JavaBean的哪个属性
1.3.3 关联查询_association定义关联对象封装规则
一对一的级联关系
数据库:通过辅助表中保存主表中的ID来关联两个表
实体类:主类中保存一个辅助类的对象
首先看一下数据库中的表之间的关系与类的之间的关系
teacher表
teacherInfo表
TeacherMapper.java
public interface TeacherMapper {
Teacher queryTeacherById(int id);
Teacher queryWithoutInfoById(int id);
}
teachermapper.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.home.java.TeacherMapper">
<!--注意Teacher里有一个TeacherInfo,现在怎么让这个TeacherInfo 和 数据库中的表对应呢 这里用到association-->
<resultMap id="tea" type="com.home.java.Teacher">
<id column="id" property="id"></id>
<result column="teanum" property="teanum"></result>
<result column="pass" property="pwd"></result>
<result column="status" property="status"></result>
<association property="teacherInfo"><!--配置另外一个对象-->
<id column="ti_id" property="id"></id> <!--这里teacherInfo表里的id就不能在写成id了,这样就冲突了,所以给他起了别名ti_id-->
<result column="t_id" property="t_id"></result>
<result column="phone" property="phone"></result>
<result column="email" property="email"></result>
<result column="teaname" property="teaname"></result>
<result column="address" property="address"></result>
<result column="jointime" property="joinTime"></result>
<result column="gender" property="gender"></result>
</association>
</resultMap>
<select id="queryTeacherById" resultMap="tea">
-- 不能写成select *
select
a.id as id,
teanum,
pass,
status,
b.id as ti_id,
t_id,
phone,
email,
teaname,
address,
jointime,
gender
from tb_teacher as a join tb_teainfo as b ON a.id = b.t_id where a.id=#{id}
</select>
<select id="queryWithoutInfoById" parameterType="int" resultType="com.home.java.Teacher">
select * from tb_teacher where id=#{id}
</select>
</mapper>
<associate>:最好设置上javaType,Mybatis可以通过associate创建一个javaType类型的对象(底层用了大量的反射。无反射,无框架)
测试:
public class Main {
public static void main(String[] args) throws IOException {
String res = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(res);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = sqlSessionFactory.openSession();
TeacherMapper teacherMapper = session.getMapper(TeacherMapper.class);
Teacher teacher = teacherMapper.queryTeacherById(29);
System.out.println(teacher.getTeacherInfo().getAddress());
}
}
1.3.4 关联查询_association分步查询
比如上面查询老师会将老师信息也查询出来,现在分为两步:
- 先按照老师id查询出老师列表
- 然后通过老师表id与老师信息表的t_id查出每个老师对应的信息
代码实现:
TeacherInfoMapper.java
public interface TeacherInfoMapper {
TeacherInfo queryTeacherInfoByid(int id);//通过id查询老师信息
}
teacherinfomapper.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.home.java.mapper.TeacherInfoMapper">
<select id="queryTeacherInfoByid" resultType="com.home.java.bean.TeacherInfo">
select * from tb_teainfo where t_id = #{id}
</select>
</mapper>
TeacherMapper.java
public interface TeacherMapper {
Teacher queryTeacherByIdStep(int id);
}
teachermapper.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="mapper.TeacherMapper">
<resultMap id="stepquery" type="com.home.java.bean.Teacher">
<id column="id" property="id"></id>
<result column="teanum" property="teanum"></result>
<result column="pass" property="pwd"></result>
<result column="status" property="status"></result>
<association property="teacherInfo" select="com.home.java.mapper.TeacherInfoMapper.queryTeacherInfoByid" column="id"></association> <!--column="id":将查询出的id列的值当作参数传给queryTeacherInfoByid方法-->
</resultMap>
<select id="queryTeacherByIdStep" resultMap="stepquery">
select * from tb_teacher where id = #{id} <!--先通过id查询老师列表,然后通过上面association调用查询老师信息的方法-->
</select>
</mapper>
column属性:指定将查询的哪一列的值传给select的方法。
如果要将多列的值传递过去,则将多列的值封装到map中传递。即column="{key1=column1,key2=column2…}"
1.3.5 关联查询_collection定义关联集合封装规则
如果一个类中保存了一个容器比如department类中有一个链表(员工列表),这时resultMap中就不能用association匹配了,因为association匹配的是一个单一的类。需要使用collection
部门与员工的映射关系:
部门
public class Department {
private int id;
private String name;
private List<Employ> employs;//一个部门有多个员工
//省略getter、setter方法
}
员工:
public class Employ {
private int id;
private String employName;
private Department department; //一个员工只有一个部门
//省略getter、setter方法
}
DepartmentMapper.java
public interface DepartmentMapper {
Department queryDepartmentById(int id);
}
departmentmapper.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="mapper.DepartmentMapper">
<resultMap id="department" type="com.home.java.bean.Department">
<id property="id" column="id"></id>
<result property="name" column="depname"></result>
<collection property="employs" ofType="com.home.java.bean.Employ"> <!--现在如果查询到的内容能和Employ匹配上,他就会将查询结果放入Employ中-->
<id column="e_id" property="id"></id>
<result column="employName" property="employName"></result>
<!--这时不再需要反向把Department查出了-->
</collection>
</resultMap>
<select id="queryDepartmentById" parameterType="int" resultMap="department">
select
a.id as id,
depname,
b.id as e_id,
employName
from tb_dep as a join tb_employ as b on a.id=b.d_id and a.id=#{id}
</select>
</mapper>
测试,核心代码:
DepartmentMapper departmentMapper = session.getMapper(DepartmentMapper.class);
Department department = departmentMapper.queryDepartmentById(1);
System.out.println(department.getEmploys().get(1).getEmployName());
ofType属性:指集合中的类型,不需要指定javaType
collection分布查询(与association分布查询相同)
总结:
-
当属性是一个对象时使用association
-
当属性是一个集合时使用collection
-
查询返回list,只需要将resultType的参数类型设置为list的泛型参数即可。
-
查出一条数据返回map,将resultType的值设置为map(mabatis为Map起了别名,如果没起别名则要写全类名)
-
查询多条数据返回map(Map<Integer,Employee>):则resultType的值设置为Employee类的全类名,且在方法上加注解@MapKey(“id”),告诉mybatis封装这个map的时候使用Employee的哪个属性作为map的key
1.4 动态SQL
1.<if test="eid != null "></if>
:通过test表达式,拼接SQL(注意这个表达式只能判断数字,不能判断字符)
2.<where>
:添加where关键字,同时会去掉多余的and(但是他只能去掉前面多出来的and或者or)
<where>
<if test="id != null">
id = #{id}
</if>
<if test="eamil != null">
and email = #{email}
</if>
<if test="gender != null">
and gender = #{gender}
</if>
</where>
<!--上面这种写法,如果id为空,mybatis会将where标签中email前的and关键字去掉。-->
<!--但是下面这种写法,如果gender为空,where标签中email后面的and关键字就不能被去掉-->
<where>
<if test="id != null">
id = #{id} and
</if>
<if test="eamil != null">
email = #{email} and
</if>
<if test="gender != null">
gender = #{gender}
</if>
</where>
<!--所以如果用where标签就最好把and写在前面。或者用trim标签可以解决这种问题-->
3.<trim prefix="" suffix="" prefixOverrides="" suffixOverrides="">
:截取并拼接
-
prefix:在操作的SQL语句前加入某些内容
-
suffix:在操作的SQL语句后加入某些内容
-
prefixOverrides:把操作的SQL语句前的某些内容去掉
-
suffixOverrides:把操作的SQL语句后的某些内容去掉
eg:设置
<trim prefix="where" suffixOverrides="and | or">
(如果产生多余的and或者or,则去掉)
4.<choose>
:选择某一个when或者otherwise执行
-
<when test="">:通过test表达式拼接SQL
-
<otherwise>:当when都不符合条件,就会选择otherwise拼接SQL
5.<foreach collection="" item="" close="" open="" separator="" index="">
:对一个数组或集合进行遍历
- collection:指定要遍历的集合或数组
- item:设置别名(将当前遍历出的元素赋值给指定的变量)
- close:设置循环体的结束内容
- open:设置循环体的内容
- separator:设置每一次循环之间的分隔符
- index:若遍历的是list,index代表下标(item就是当前值);若遍历的是map,index代表键(item就是map的值)
eg:批量删除(传入的参数是字符串:“1,2,3”)
<delete id="deleteMpreEmp">
delete from emp where eid in (${value})
/*注意不能用#{},因为#{}用的是通配符?赋值,会给参数默认加单引号,这时只会删除第一个参数,即eid为1的值*/
</delete>
eg:foreach实现批量删除(传入的参数是链表)
<delete id="deleteMoreByList" parameterType="java.util.List">
delete from emp where eid in
<foreach collection="list" open="(" item="eid" separator="," close=")">
#{eid}
</foreach>
</delete>
实现批量修改:
<update id="updateMoreByArray">
<foreach collection="emps" item="emp">
update emp set ename = #{emp.ename}, age = #{emp.age}, sex = #{emp.sex} where eid = #{emp.eid}; /*这样一次会执行多条sql语句*/
</foreach>
</update>
把每条数据修改为对应内容,注意:必须在连接地址(url)后添加参数allowMultiQueries=true(允许一次执行多条sql语句)
6.<sql id="">
:设置一段SQL片段,即公共SQL,可以被当前映射文件中所有的SQL语句所访问,使用时用include标签
-
<include refid="">:访问某个SQL片段
include还可以定义一些property,sql标签内部就能使用自定义的属性。取值方式为${prop-name}
eg:
<sql id="empColumns">
eid,ename,age,sex,did,${testColumn}
</sql>
<select id="getEmpByEid" resultType="com.home.java.Emp">
select
<include refid="empColumns">
<property name="testColumn" value="abc"/>
</include>
from emp
where eid = #{eid}
</select>
Mybatis学习内容持续更新中…