7、ORM映射
ORM(Object Relational Mapping)对象关系映射,解决面向对象与关系数据库存在的互不匹配的现象的技术,将程序中的对象自动持久化到关系数据库中。
可以看到,实体类中的属性和数据库中的字段根据命名一一对应。
7.1、解决属性与字段名不一致
上面我们了解到,属性和字段根据相同的命名一一映射,假设名字不一致呢?
比如现有一个学生表s_student和学生实体类:
可以看到,属性和字段名并不一样,属性是按照java命名规范的驼峰命名,字段是按照sql规范的下划线命名。
这种情况有三种解决方法:
1、通过字段别名来解决
可以利用sql语句为字段取与属性名相同的别名,来与属性名对应。
<select id="selectAllStudent" resultType="Student">
select s_id sId,s_name sName,s_age sAge,s_sex sSex,s_email sEmail,s_class sClass
from t_student
</select>
2、通过配置全局设置mapUnderscoreToCamelCase(有前提条件)
mapUnderscoreToCamelCase全局设置是用来解决java的驼峰命名与sql的下划线命名规范的。所以要使用这个方法前提是,数据库字段命名必须用规范的下划线命名同时实体类的属性命名必须按照驼峰规范命名。
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
在满足前提条件的情况下,在核心配置中加入如上配置即可。
3、通过resultMap自定义映射
在接口对应的映射文件中自定义resultMap来解决:
<resultMap id="studentresultMap" type="Student">
<id property="sId" column="s_id"></id>
<result property="sName" column="s_name"></result>
<result property="sAge" column="s_age"></result>
<result property="sSex" column="s_sex"></result>
<result property="sEmail" column="s_email"></result>
<result property="sClass" column="s_class"></result>
</resultMap>
<select id="selectAllStudent" resultMap="studentresultMap">
select * from t_student
</select>
如上:id
标签代表主键;property
代表实体类的属性名,column
代表数据库中的字段名。
自定义映射后,在相应的sql语句中引入 resultMap=""
即可。
7.2、解决多对一的映射
现有两张表:
学生表:t_student
班级表:t_class
在学生表中,设置一个逻辑外键s_class存放班级id就可以通过联表查询获取到班级信息。
那么在学生实体类中,要想存放班级信息属于多对一的映射。班级信息----------->学生的一个属性。
可以在学生实体类中设置一个班级实体类作为一个属性,用来存放班级信息。
具体映射有三种方法:
1、级联映射
对应的映射文件:
<resultMap id="selectAllStudentAndClassMap" type="Student">
<id property="sId" column="s_id"></id>
<result property="sName" column="s_name"></result>
<result property="sAge" column="s_age"></result>
<result property="sSex" column="s_sex"></result>
<result property="sEmail" column="s_email"></result>
<result property="sClass.cId" column="c_id"></result>
<result property="sClass.cName" column="c_name"></result>
</resultMap>
<!-- List<Student> selectAllStudentAndClass(@Param("sId") Integer sId);-->
<select id="selectAllStudentAndClass" resultMap="selectAllStudentAndClassMap">
select *
from t_student s
left join t_class c on s.s_class = c.c_id
where s.s_id = #{sId}
</select>
2、association标签
association是用来解决多对一映射的标签,对应的映射文件:
<resultMap id="selectAllStudentAndClassMapTwo" type="Student">
<id property="sId" column="s_id"></id>
<result property="sName" column="s_name"></result>
<result property="sAge" column="s_age"></result>
<result property="sSex" column="s_sex"></result>
<result property="sEmail" column="s_email"></result>
<association property="sClass" javaType="cClass">
<id property="cId" column="c_id"></id>
<result property="cName" column="c_name"></result>
</association>
</resultMap>
<!-- List<Student> selectAllStudentAndClass(@Param("sId") Integer sId);-->
<select id="selectAllStudentAndClass" resultMap="selectAllStudentAndClassMapTwo">
select *
from t_student s
left join t_class c on s.s_class = c.c_id
where s.s_id = #{sId}
</select>
association中的property
属性代表实体类中的属性名,javaType
代表该属性的类型。
3、分步查询
对应的映射文件:
<resultMap id="selectAllStudentByStepMap" type="Student">
<id property="sId" column="s_id"></id>
<result property="sName" column="s_name"></result>
<result property="sAge" column="s_age"></result>
<result property="sSex" column="s_sex"></result>
<result property="sEmail" column="s_email"></result>
<association property="sClass"
select="mappers.ClassMapper.selectById"
column="s_class">
</association>
</resultMap>
<!-- Student selectAllStudentByStep(@Param("sId") Integer sId);-->
<select id="selectAllStudentByStep" resultMap="selectAllStudentByStepMap">
select *
from t_student
where s_id = #{sId}
</select>
也就是先查基本属性,在调用班级接口函数查班级属性。注意association中的column
标签要填写数据库中的逻辑外键字段名(等价于第二步查询时传入的参数)。官方文档解释为:
The column name from the database, or the aliased column label that holds the value that will be passed to the nested statement as an input parameter. This is the same string that would normally be passed to resultSet.getString(columnName). Note: To deal with composite keys, you can specify multiple column names to pass to the nested select statement by using the syntax column=“{prop1=col1,prop2=col2}”. This will cause prop1 and prop2 to be set against the parameter object for the target nested select statement
数据库中的列名,或保存将作为输入参数传递给嵌套语句的值的别名列标签。这个字符串与通常传递给 result Set.getString (column nName)的字符串相同。注意: 要处理复合键,可以使用语法 column = “{ prop1 = col1,prop2 = col2}”指定多个列名来传递给嵌套的 select 语句。这将导致将 prop1和 prop2设置为目标嵌套 select 语句的参数对象。
第二步所调用的查询语句:
<!-- int selectById(@Param("cId") Integer cId);-->
<select id="selectById" resultType="cClass">
select *
from t_class
where c_id = #{cId}
</select>
7.3、解决一对多的映射
上面提到的班级信息的多个字段要存放在一个学生实体类属性中,叫做多对一映射。
那一个班级实体类属性中要存放多个学生记录,叫做一对多映射。
这里采用在班级实体类中设置一个List集合来存放学生。
两种映射方法:
1、collection标签
对应的映射文件:
<resultMap id="selectClassByIdMap" type="cClass">
<id property="cId" column="c_id"></id>
<result property="cName" column="c_name"></result>
<collection property="students" ofType="Student">
<id property="sId" column="s_id"></id>
<result property="sName" column="s_name"></result>
<result property="sAge" column="s_age"></result>
<result property="sSex" column="s_sex"></result>
<result property="sEmail" column="s_email"></result>
</collection>
</resultMap>
<!-- cClass selectClassById(@Param("cId") Integer cId);-->
<select id="selectClassById" resultMap="selectClassByIdMap">
select *
from t_class c
left join t_student s on c.c_id = s.s_class
where c.c_id = #{cId}
</select>
要注意区分collection和association的区别,collection用ofType=""标签表示集合中的实体类型。
查询id为1的班级信息,运行结果:
cClass{cId=1, cName='计算机1班', students=[
Student{sId=1, sName='张三', sAge=20, sSex='男', sEmail='123@qq.com', sClass=null},
Student{sId=4, sName='赵六', sAge=20, sSex='男', sEmail='123@qq.com', sClass=null}]
}
2、分步查询
相应的映射文件:
<resultMap id="selectClassByStepMap" type="cClass">
<id property="cId" column="c_id"></id>
<result property="cName" column="c_name"></result>
<collection property="students"
select="mappers.StudentMapper.selectById"
column="c_id"></collection>
</resultMap>
<!-- cClass selectClassByStep(@Param("cId") Integer cId);-->
<select id="selectClassByStep" resultMap="selectClassByStepMap">
select *
from t_class
where c_id = #{cId}
</select
调用的第二步查询语句:(注意返回类型为List)
<!-- List<Student> selectById(@Param("sId") Integer sId);-->
<select id="selectById" resultType="Student">
select *
from t_student
where s_class = #{sId}
</select>
7.4、分步查询之延迟加载
可以看到,无论是一对多映射还是多对一映射,都可以用分步查询,其中分步查询有一个优势就是延迟加载,也就是懒汉式加载(用什么加载什么,不用不加载)。
两种开启方式:
1、配置核心文件
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
在MyBatis核心配置文件中添加全局设置,开启延迟加载。
2、fetchType标签
在association或者collection中设置fetchType='lazy'
可以达到同样的效果。