继续原创输入,点击上方蓝字关注我吧
目录
- 前言
- 什么是后果映射?
如何映射?
- 别名映射
驼峰映射
- 配置文件开启驼峰映射
- 配置类中开启驼峰映射
- resultMap映射
- 总结
高级后果映射
关联(association)
- 例子
- 关联的嵌套 Select 查问
- 关联的嵌套后果映射
- 总结
汇合collection
- 汇合的嵌套 Select 查问
- 汇合的嵌套后果映射
- 总结
前言
- 上一篇文章介绍了Mybatis根底的CRUD操作、罕用的标签、属性等内容,如果对局部不相熟的敌人能够看Mybatis入门之基本操作。
- 本篇文章持续解说Mybatis的后果映射的内容,想要在企业开发中灵便的应用Mybatis,这部分的内容是必须要精通的。
什么是后果映射?
- 简略的来说就是一条
SQL查问语句
返回的字段如何与Java实体类
中的属性绝对应。 - 如下一条SQL语句,查问患者的用户id,科室id,主治医生id:
<select id='selectPatientInfos' resultType='com.xxx.domain.PatientInfo'> select user_id,dept_id,doc_id from patient_info; </select>
- Java实体类
PatientInfo
如下:
@Datapublic class PatientInfo{ private String userId; private String deptId; private String docId;}
- 程序员写这条SQL的目标就是想查问进去的
user_id
,dept_id
,doc_id
别离赋值给实体类中的userId
,deptId
,docId
。这就是简略的后果映射。
如何映射?
- Myabtis中的后果映射有很多种形式,上面会逐个介绍。
别名映射
- 这个简略,放弃查问的SQL返回的字段和Java实体类一样即可,比方下面例子的SQL能够写成:
<select id='selectPatientInfos' resultType='com.xxx.domain.PatientInfo'> select user_id as userId, dept_id as deptId, doc_id as docId from patient_info; </select>
- 这样就能和实体类中的属性映射胜利了。
驼峰映射
- Mybatis提供了驼峰命名映射的形式,比方数据库中的
user_id
这个字段,可能主动映射到userId
属性。那么此时的查问的SQL变成如下即可:
<select id='selectPatientInfos' resultType='com.xxx.domain.PatientInfo'> select user_id,dept_id,doc_id from patient_info; </select>
- 如何开启呢?与SpringBoot整合后开启其实很简略,有两种形式,一个是配置文件中开启,一个是配置类开启。
配置文件开启驼峰映射
- 只须要在
application.properties
文件中增加如下一行代码即可:
mybatis.configuration.map-underscore-to-camel-case=true
配置类中开启驼峰映射【简略理解,后续源码章节着重介绍】
- 这种形式须要你对源码有肯定的理解,上一篇入门教程中有提到,Mybatis与Springboot整合后适配了一个starter,那么必定会有主动配置类,Mybatis的主动配置类是
MybatisAutoConfiguration
,其中有这么一段代码,如下:
@ConditionalOnMissingBean
这个注解的意思就是当IOC容器中没有SqlSessionFactory
这个Bean对象这个配置才会失效;applyConfiguration(factory)
这行代码就是创立一个org.apache.ibatis.session.Configuration
赋值给SqlSessionFactoryBean
。源码剖析到这,应该很分明了,无非就是本人在容器中创立一个SqlSessionFactory
,而后设置属性即可,如下代码:
@Bean("sqlSessionFactory") public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); //设置数据源 sqlSessionFactoryBean.setDataSource(dataSource); //设置xml文件的地位 sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATOIN)); //创立Configuration org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); // 开启驼峰命名映射 configuration.setMapUnderscoreToCamelCase(true); configuration.setDefaultFetchSize(100); configuration.setDefaultStatementTimeout(30); sqlSessionFactoryBean.setConfiguration(configuration); //将typehandler注册到mybatis sqlSessionFactoryBean.setTypeHandlers(typeHandlers()); return sqlSessionFactoryBean.getObject(); }
- 留神:如果对
SqlSessionFactory
没有非凡定制,不介意重写,因为这会主动笼罩主动配置类中的配置。
resultMap映射
- 什么是
resultMap
?简略的说就是一个相似Map的构造,将数据库中的字段和JavaBean中的属性字段对应起来,这样就能做到一一映射了。 - 上述的例子应用resultMap又会怎么写呢?如下:
<!--创立一个resultMap映射--><resultMap id="patResultMap" type="com.xxx.domain.PatientInfo"> <id property="userId" column="user_id" /> <result property="docId" column="doc_id"/> <result property="deptId" column="dept_id"/></resultMap><!--应用resultMap映射后果到com.xxx.domain.PatientInfo这个Bean中--><select id='selectPatientInfos' resultMap='patResultMap'> select user_id,dept_id,doc_id from patient_info; </select>
- 其实很简略,就是创立一个
<resultMap>
,而后<select>
标签指定这个resultMap即可。 <resultMap>
的属性如下:id
:惟一标识这个resultMap,同一个Mapper.xml中不能反复type
:指定JavaBean的类型,能够是全类名,也能够是别名
子标签
<result>
的属性如下:column
:SQL返回的字段名称property
:JavaBean中属性的名称javaType
:一个 Java 类的全限定名,或一个类型别名(对于内置的类型别名,能够参考下面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常能够推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保障行为与冀望的相一致。jdbcType
:JDBC 类型,所反对的 JDBC 类型参见这个表格之后的“反对的 JDBC 类型”。 只须要在可能执行插入、更新和删除的且容许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你间接面向 JDBC 编程,你须要对能够为空值的列指定这个类型。typeHandler
: 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。resultMap
:后果映射的 ID,能够将此关联的嵌套后果集映射到一个适合的对象树中。 它能够作为应用额定 select 语句的代替计划。
总结
- 以上列举了三种映射的形式,别离是别名映射,驼峰映射、
resultMap
映射。 - 你认为这就完结了?要是世界这么简略多好,做梦吧,哈哈!!!
高级后果映射
- MyBatis 创立时的一个思维是:数据库不可能永远是你所想或所需的那个样子。 咱们心愿每个数据库都具备良好的第三范式或 BCNF 范式,惋惜它们并不都是那样。 如果能有一种数据库映射模式,完满适配所有的应用程序,那就太好了,但惋惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
- 咱们晓得在数据库的关系中一对一,多对一,一对多,多对多的关系,那么这种关系如何在Mybatis中体现并映射胜利呢?
关联(association)
- 关联(association)元素解决有一个类型的关系。 比方,在咱们的示例中,一个员工属于一个部门。关联后果映射和其它类型的映射工作形式差不多。 你须要指定指标属性名以及属性的
javaType
(很多时候 MyBatis 能够本人推断进去),在必要的状况下你还能够设置JDBC
类型,如果你想笼罩获取后果值的过程,还能够设置类型处理器。 关联的不同之处是,你须要通知 MyBatis 如何加载关联。MyBatis 有两种不同的形式加载关联:
嵌套 Select 查问
:通过执行另外一个 SQL 映射语句来加载冀望的简单类型。嵌套后果映射
:应用嵌套的后果映射来解决连贯后果的反复子集。
首先,先让咱们来看看这个元素的属性。你将会发现,和一般的后果映射相比,它只在
select
和resultMap
属性上有所不同。property
: 映射到列后果的字段或属性。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被应用。javaType
:一个 Java 类的齐全限定名,或一个类型别名(对于内置的类型别名,能够参考下面的表格)jdbcType
: JDBC 类型, 只须要在可能执行插入、更新和删除的且容许空值的列上指定 JDBC 类型typeHandler
:应用这个属性,你能够笼罩默认的类型处理器。 这个属性值是一个类型处理器实现类的齐全限定名,或者是类型别名。column
: 数据库中的列名,或者是列的别名。个别状况下,这和传递给resultSet.getString(columnName)
办法的参数一样。 留神:在应用复合主键的时候,你能够应用column="{prop1=col1,prop2=col2}"
这样的语法来指定多个传递给嵌套 Select 查问语句的列名。这会使得prop1
和prop2
作为参数对象,被设置为对应嵌套 Select 语句的参数。select
:用于加载简单类型属性的映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给指标 select 语句。 具体请参考上面的例子。留神:在应用复合主键的时候,你能够应用column="{prop1=col1,prop2=col2}"
这样的语法来指定多个传递给嵌套 Select 查问语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。fetchType
:可选的。有效值为lazy
和eager
。 指定属性后,将在映射中疏忽全局配置参数lazyLoadingEnabled
,应用属性的值。
例子
- 一对一的关系比方:一个员工属于一个部门,那么数据库表就会在员工表中加一个部门的id作为逻辑外键。
- 创立员工JavaBean
@Datapublic class User { private Integer id; private String username; private String password; private Integer age; private Integer deptId; //部门 private Department department; }
- 部门JavaBean
@Datapublic class Department { private Integer id; private String name;}
- 那么咱们想要查问所有的用户信息和其所在的部门信息,此时的sql语句为:
select * from user u left join department d on u.department_id=d.id
;。然而咱们在mybaits中如果应用这条语句查问,那么返回的后果类型是什么呢?如果是User类型的,那么查问后果返回的还有Department
类型的数据,那么必定会对应不上的。此时<resultMap>
来了,它来了!!!
关联的嵌套 Select 查问【能够疏忽】
- 查问员工和所在的部门在Mybatis如何写呢?代码如下:
<resultMap id="userResult" type="com.xxx.domain.User"> <id column="id" property="id"/> <result column="password" property="password"/> <result column="age" property="age"/> <result column="username" property="username"/> <result column="dept_id" property="deptId"/> <!--关联查问,select嵌套查问--> <association property="department" column="dept_id" javaType="com.xxx.domain.Department" select="selectDept"/></resultMap><!--查问员工--><select id="selectUser" resultMap="userResult"> SELECT * FROM user WHERE id = #{id}</select><!--查问部门--><select id="selectDept" resultType="com.xxx.domain.Department "> SELECT * FROM department WHERE ID = #{id}</select>
- 就是这么简略,两个select语句,一个用来加载员工,一个用来加载部门。
这种形式尽管很简略,但在大型数据集或大型数据表上体现不佳。这个问题被称为
N+1
查问问题。 概括地讲,N+1 查问问题是这样子的:- 你执行了一个独自的 SQL 语句来获取后果的一个列表(就是
+1
)。 - 对列表返回的每条记录,你执行一个
select
查问语句来为每条记录加载详细信息(就是N
)。
- 你执行了一个独自的 SQL 语句来获取后果的一个列表(就是
- 这个问题会导致成千盈百的 SQL 语句被执行。有时候,咱们不心愿产生这样的结果。
关联的嵌套后果映射【重点】
<association >
标签中还能够间接嵌套后果映射,此时的Mybatis的查问如下:
<!-- 定义resultMap --><resultMap id="UserDepartment" type="com.xxx.domain.User" > <id column="user_id" property="id"/> <result column="password" property="password"/> <result column="age" property="age"/> <result column="username" property="username"/> <result column="dept_id" property="deptId"/> <!-- property: 指定User中对应的部门属性名称 javaType: 指定类型,能够是全类名或者别名 --> <association property="department" javaType="com.xx.domain.Department"> <!--指定Department中的属性映射,这里也能够应用独自拎进去,而后应用association中的resultMap属性指定--> <id column="id" property="id"/> <result column="dept_name" property="name"/> </association></resultMap><!-- resultMap: 指定下面resultMap的id的值 --> <select id="findUserAndDepartment" resultMap="UserDepartment"> select u.id as user_id, u.dept_id, u.name, u.password, u.age, d.id, d.name as dept_name from user u left join department d on u.department_id=d.id </select>
总结
- 至此
有一个
类型的关联曾经实现了,学会一个<association>
应用即能实现。 - 留神: 关联的嵌套 Select 查问不倡议应用,
N+1
是个重大问题,虽说Mybatis提供了提早加载的性能,然而依然不倡议应用,企业开发中也是不罕用的。
汇合collection
- 汇合,顾名思义,就是解决
有很多个
类型的关联。 - 其中的属性和
association
中的属性相似,不再反复了。 - 比方这样一个例子:查问一个部门中的全副员工,查问SQL如何写呢?如下:
select * from department d left join user u on u.department_id=d.id;
- 此时的
User
实体类如下:
@Datapublic class User { private Integer id; private String username; private String password; private Integer age; private Integer deptId; }
- 此时的
Department
实体类如下:
@Datapublic class Department { private Integer id; private String name; private List<User> users;}
- 和
association
相似,同样有两种形式,咱们能够应用嵌套 Select 查问,或基于连贯的嵌套后果映射汇合。
汇合的嵌套 Select 查问【能够疏忽】
- 不太重要,查问如下:
<resultMap id="deptResult" type="com.xxx.domain.Department"> <!--指定Department中的属性映射,这里也能够应用独自拎进去,而后应用association中的resultMap属性指定--> <id column="id" property="id"/> <result column="name" property="name"/> <!-- ofType:指定理论的JavaBean的全类型或者别名 select:指定嵌套的select查问 javaType:汇合的类型,能够不写,Mybatis能够揣测进去--> <collection property="users" javaType="java.util.ArrayList" column="id" ofType="com.xxx.doamin.User" select="selectByDeptId"/></resultMap><select id="selectDept" resultMap="deptResult"> SELECT * FROM department WHERE ID = #{id}</select><select id="selectByDeptId" resultType="com.xxx.domain.User"> SELECT * FROM user WHERE dept_id = #{id}</select>
- 留神:这里呈现了一个不同于
association
的属性ofType
,这个属性十分重要,它用来将 JavaBean(或字段)属性的类型和汇合存储的类型辨别开来。
汇合的嵌套后果映射【重点】
- 当初你可能曾经猜到了汇合的嵌套后果映射是怎么工作的——除了新增的
ofType
属性,它和关联的完全相同。 - 此时的Mybatis查问如下:
<!--部门的resultMap--><resultMap id="deptResult" type="com.xxx.domain.Department"> <!--指定Department中的属性映射,这里也能够应用独自拎进去,而后应用association中的resultMap属性指定--> <id column="dept_id" property="id"/> <result column="dept_name" property="name"/> <!-- ofType:指定理论的JavaBean的全类型或者别名 resultMap:指定员工的resultMap--> <collection property="users" ofType="com.xxx.doamin.User" resultMap='userResult'/></resultMap><!--员工的resultMap--><resultMap id="userResult" type="com.xxx.domain.User"> <id column="user_id" property="id"/> <result column="password" property="password"/> <result column="age" property="age"/> <result column="username" property="username"/></resultMap><select id="selectDeptById" resultType="com.xxx.domain.Department"> select d.id as dept_id, d.name as dept_name, u.id as user_id, u.password, u.name from department d left join user u on u.department_id=d.id where d.id=#{id}</select>
总结
- 至此Mybatis第二弹之后果映射曾经写完了,如果感觉作者写的不错,给个在看关注一波,后续还有更多精彩内容推出。