上一篇博客的传送门:MyBatis 入门到精通(三)
MyBatis 的核心点在于映射,顾名思义,将数据库表的数据和 Java 实体类进行一一对应,那么对应的原则是什么呢?
当我们执行查询 *** 作时,通过查看日志我们看到如下效果:
从数据库中查询了一条数据,而我们在后边输出的 Emp 对象时,发现大部分内容都有值,只有 ename1 为 null,这是为什么呢?
答:数据库查询出来的虚拟列和 Java 实体类中的属性名一致(不区分大小写),就可以自动映射。
虚拟列名和属性名不一致如果我们的查询出来的虚拟列和属性名不一致如何解决呢?
别名查询从 SQL 语句入手,给查询的列起一个别名,别名和属性名一致就行。
select emp.*, ename as 'ename1' from emp where empstate = 1
手动映射
ResultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的 *** 作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 ResultMap 能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
ResultMap 的属性列表:
属性 | 描述 |
---|---|
id | 当前命名空间中的一个唯一标识,用于标识一个结果映射。 |
type | 类的完全限定名, 或者一个类型别名(关于内置的类型别名,可以参考上面的表格)。 |
autoMapping | 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。 |
id|result 子元素内容
这些元素是结果映射的基础。id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。
这两者之间的唯一不同是,id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。
Id 和 Result 的属性列表:
属性 | 描述 |
---|---|
property | 映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 |
column | 数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 |
javaType | 一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType | JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。 |
typeHandler | 我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。 |
支持的 JDBC 类型
为了以后可能的使用场景,MyBatis 通过内置的 jdbcType 枚举类型支持下面的 JDBC 类型。
BIT | FLOAT | CHAR | TIMESTAMP | OTHER | UNDEFINED |
TINYINT | REAL | VARCHAR | BINARY | BLOB | NVARCHAR |
SMALLINT | DOUBLE | LONGVARCHAR | VARBINARY | CLOB | NCHAR |
INTEGER | NUMERIC | DATE | LONGVARBINARY | BOOLEAN | NCLOB |
BIGINT | DECIMAL | TIME | NULL | CURSOR | ARRAY |
实际使用中,我们会涉及多张表的 *** 作,它们之间存在一定的关系,比较常见的有多对一和一对多关系。
多对一关系以 dept(部门) 和 emp(员工)来举例子,一个员工隶属于一个部门,一个部门有多个员工,那么员工和部门之间存在多对一的关系。在 java 中我们使用两种方式来处理这个问题:
- 关联
- 拼接
比如,在我们的示例中,一个员工有一个部门。关联结果映射和其它类型的映射工作方式差不多。 你需要指定目标属性名以及属性的javaType(很多时候 MyBatis 可以自己推断出来),在必要的情况下你还可以设置 JDBC 类型,如果你想覆盖获取结果值的过程,还可以设置类型处理器。
关联的不同之处是,你需要告诉 MyBatis 如何加载关联。 它的相关属性是:
属性 | 描述 |
---|---|
column | 数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。 |
property | 映射到列结果的字段或属性。 |
javaType | 一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 |
select | 用于加载复杂类型属性的映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。 具体请参考下面的例子。注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。 |
fetchType | 可选的。有效值为 lazy 和 eager。 指定属性后,将在映射中忽略全局配置参数 lazyLoadingEnabled,使用属性的值。 |
实际的查询 *** 作是:
关联是通过表连接的方式来查询数据,只连接一次数据库,能有效的节约连接资源,就可以将多张表的数据获取出来,在通过 resultMap 的 association 将数据“关联”起来。
拼接方式处理关联方式可以有效节约连接资源,但随之产生的笛卡尔问题却不可避免,那么我们可以多次查询数据库,将多个结果获取出来后封装到一个对象中。
查询员工
查询这个员工的部门
组装到一起
EmpMapper mapper = session.getMapper(EmpMapper.class);
DeptMapper mapper2 = session.getMapper(DeptMapper.class);
Emp emp = mapper.findEmpByNo(7566);
Dept dept = mapper2.findDeptByNo(emp.getDeptNo());
emp.setDept(dept);
session.close();
组装方式可以避免笛卡尔积问题,但是多次连接数据库也会耗费资源,具体应用可以根据实际来使用。
一对多关系如果说,员工对于部门是多对一,那么部门对于员工就是一对多。一对多也是一样的两种方式
- 集合
- 拼接
和关联比较接近,只不过返回的结果有多条需要通过集合进行封装。
collection 的属性和 association 基本一致,引用方式也一致,这里不在特别说明。
拼接方式也是查询多次,一次查询部门信息,一次查询这个部门的所有员工信息,将它们组装到一起。
这里只引用组装的代码
SqlSession session = ssf.openSession();
EmpMapper mapper = session.getMapper(EmpMapper.class);
DeptMapper mapper2 = session.getMapper(DeptMapper.class);
// 只能查询到部门信息
Dept dept = mapper2.findDeptByNo(10);
Set empSet = mapper.findEmpByDeptNo(10);
// 组装
dept.setEmpSet(empSet);
对关联或集合的映射,并没有深度、广度或组合上的要求。但在映射时要留意性能问题。 在探索最佳实践的过程中,应用的单元测试和性能测试会是你的好帮手。 而 MyBatis 的好处在于,可以在不对你的代码引入重大变更(如果有)的情况下,允许你之后改变你的想法。
高级关联和集合映射是一个深度话题。章节的介绍只能到此为止。配合少许的实践,你会很快了解全部的用法。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)