简介

在 MyBatis 中,ObjectWrapper 是一个关键的接口,用于详细封装了对象的属性信息。ObjectWrapper 主要用于内部操作,它抽象了对象的属性操作,使得 MyBatis 能够统一处理原生类型、Bean 对象以及 Map 集合等。

类图展示

【mybatis】objectwrapper解读-LMLPHP

主要功能

  1. 属性的获取与设置ObjectWrapper 提供了方法来获取和设置对象的属性值。这对于 MyBatis 在执行 SQL 语句时,动态地插入参数值或者将查询结果映射回对象是非常有用的。
  2. 集合操作的支持:对于 Map 类型或者集合类型的对象,ObjectWrapper 提供了方法来操作集合内的元素,包括获取指定键的值等。
  3. 复杂属性的处理ObjectWrapper 还能处理复杂的属性路径。例如,如果有一个属性路径是 someBean.somePropertyObjectWrapper 能够解析这个路径并获取或设置相应的属性值。

实现类

MyBatis 提供了几个 ObjectWrapper 的实现类,用于处理不同类型的对象:

  • **BeanWrapper**:用于处理 Java Bean 对象的包装器,通过反射机制访问对象的属性。
  • **MapWrapper**:用于处理 Map 类型对象的包装器,通过键值对来访问数据。
  • **CollectionWrapper**:尽管不直接实现 ObjectWrapper 接口,但 MyBatis 处理集合类型时有相关的机制,以支持集合的处理。

应用场景

ObjectWrapper 的设计主要是为了内部使用,以提供一种统一的方式来访问对象的属性,无论这些对象是普通的 Java Beans、Map 集合还是其他复杂的数据结构。这种机制极大地简化了 MyBatis 的参数映射和结果映射逻辑,使得开发者可以更加专注于 SQL 语句的编写,而不是数据的封装和解析过程。

在自定义类型处理器(Type Handler)或是高级映射配置时,了解 ObjectWrapper 的工作原理可以帮助开发者更好地控制 MyBatis 的行为,实现更加灵活和强大的数据操作。

ObjectWrapper 接口类的设计理念

封装性(Encapsulation)

ObjectWrapper 封装了对对象属性的操作,隐藏了具体的实现细节。这意味着,不论是操作 Java Bean、Map 还是其他任何复杂对象,对于使用者来说,都是通过一个统一的接口进行。这种封装性使得 MyBatis 的其余部分可以不用关心对象属性是如何被访问和修改的,从而降低了各个模块间的耦合度。

抽象性(Abstraction)

ObjectWrapper 作为一个高层次的抽象,定义了一套操作对象属性的通用方法。这种抽象使得 MyBatis 能够支持广泛的数据类型,而无需为每种数据类型编写特定的代码。通过抽象出一个统一的接口,MyBatis 可以轻松地扩展以支持新的对象类型,只需添加相应类型的 ObjectWrapper 实现即可。

单一职责原则(Single Responsibility Principle)

ObjectWrapper 的设计遵循了单一职责原则,即一个类应该只有一个引起变化的原因。ObjectWrapper 接口专注于对象属性的访问和操作,而不涉及其他逻辑,如 SQL 语句的构建、数据库连接的管理等。这使得 ObjectWrapper 及其实现类易于理解和维护。

开闭原则(Open/Closed Principle)

根据开闭原则,软件实体应该对扩展开放,对修改关闭。ObjectWrapper 接口的设计允许 MyBatis 在不修改已有代码的基础上扩展框架的功能。通过添加新的 ObjectWrapper 实现,可以支持新的数据类型或自定义的对象处理逻辑,而无需改动框架的核心部分。

策略模式(Strategy Pattern)

ObjectWrapper 的设计体现了策略模式的应用,其中 ObjectWrapper 接口定义了一组算法家族(在这里是对象属性的操作方法),而它的实现类则封装了这些算法的具体实现。这使得 ObjectWrapper 在运行时可以根据对象的类型选择合适的实现,从而改变对象属性的访问和操作行为。这种模式提高了代码的灵活性和可重用性。

BaseWrapper类解读

MyBatis 中的 BaseWrapper 类并不是直接暴露给终端用户的一个类,而是作为一个内部使用的基础类,提供了一些共通的功能和工具,供其子类如 BeanWrapperMapWrapperObjectWrapper 的实现类使用。BaseWrapper 主要是实现了 ObjectWrapper 接口的一些公共逻辑,以避免在每一个实现中重复相同的代码。它是一个抽象类,为处理对象属性的读取和设置提供了基础设施。

主要功能
  1. 属性表达式解析BaseWrapper 提供了解析复杂属性表达式的功能,例如嵌套属性和集合/数组元素的访问。这允许 MyBatis 在运行时动态地访问和修改对象的属性,即使这些属性是通过复杂的路径访问的。
  2. 通用属性访问:通过封装对反射 API 的调用,BaseWrapper 提供了一种相对简单的方式来读取和设置对象的属性值。这包括处理 Java Beans、Map 以及集合类型的对象。
  3. 缓存机制:为了提高性能,BaseWrapper 可能会实现一些缓存机制,例如缓存反射操作的结果(如方法、字段访问)。这样可以减少对相同属性的重复解析和访问开销。
  4. 类型转换和处理BaseWrapper 提供了类型转换的基础设施,以便在设置属性值时,能够将输入数据转换为目标属性的适当类型。这是通过 MyBatis 的类型处理器(TypeHandler)来实现的。
  5. 延迟加载支持:在一些情况下,BaseWrapper 可能会参与支持延迟加载(Lazy Loading)的逻辑,尽管这通常是在更高层次上处理的,但 BaseWrapper 提供的基础设施使得这种功能的实现变得可能。
应用场景

BaseWrapper 主要在 MyBatis 内部使用,作为一个工具类来支持更高层次的抽象,如 ObjectWrapper 的实现。它通过提供一组通用的工具和方法简化了对不同类型对象的操作,提高了 MyBatis 在处理复杂映射和动态 SQL 时的灵活性和效率。用户通常不需要直接与 BaseWrapper 交互,但理解其功能和作用可以帮助更好地理解 MyBatis 是如何在内部处理对象映射的。

类定义和构造函数
  • BaseWrapper 是一个抽象类,它实现了 ObjectWrapper 接口。
  • 它包含一个 protectedMetaObject 类型的成员变量 metaObjectMetaObject 是 MyBatis 中用于反射操作的核心类,提供了对对象属性的访问和修改功能。
  • 构造函数 BaseWrapper(MetaObject metaObject) 用于初始化 metaObject 成员变量。
方法解读
  • **resolveCollection(PropertyTokenizer prop, Object object)**** 方法**:用于处理集合对象。如果 PropertyTokenizername 属性为空字符串,表示直接返回输入对象;否则,通过 metaObject.getValue(prop.getName()) 获取属性值。这里的 PropertyTokenizer 是用于分析属性表达式的工具类。
  • **getCollectionValue(PropertyTokenizer prop, Object collection)**** 方法**:根据 PropertyTokenizer 中的索引,从集合对象中获取元素值。这个方法能够处理 MapList、数组以及基本类型数组(如 int[], char[] 等)。如果集合对象不是这些类型之一,则抛出 ReflectionException 异常。
  • **setCollectionValue(PropertyTokenizer prop, Object collection, Object value)**** 方法**:根据 PropertyTokenizer 中的索引,向集合对象中设置元素值。这个方法同样能够处理 MapList、数组以及基本类型数组。它根据集合的类型,将值 value 插入或替换到正确的位置。如果操作的目标不是这些集合类型之一,同样会抛出 ReflectionException 异常。

CollectionWrapper类解读

核心功能
  • 构造函数:接受一个 CustomCollection 类型的对象作为参数,并将其存储在 collection 成员变量中。这表明 CustomObjectWrapper 是专门为操作 CustomCollection 设计的。
  • **isCollection**** 方法**:返回 true,表明这个包装器封装的是一个集合对象。这是对 ObjectWrapper 接口的具体实现,指示 MyBatis 这个包装器处理的是集合类型的数据。
  • **add**** 方法和 **addAll** 方法**:这两个方法允许向 collection 成员变量中添加单个元素或多个元素。这表明 CustomObjectWrapper 提供了基本的集合操作功能,使得可以通过这个包装器修改底层集合。
未实现的方法

代码中有多个方法标记为 “Not Implemented”,包括 getsetfindPropertygetGetterNamesgetSetterNamesgetSetterTypegetGetterTypehasSetterhasGetterinstantiatePropertyValue。这些方法的空实现表明 CustomObjectWrapper 在当前形态下不支持这些操作。这可能是因为这些操作对于 CustomCollection 类型的数据不是必需的,或者开发者打算在未来根据需要实现它们。

设计意图

CustomObjectWrapper 的设计意图是提供一个机制,通过它 CustomCollection 类型的对象可以在 MyBatis 中使用,同时提供了一种方式来扩展 MyBatis 默认的对象包装行为。通过实现 ObjectWrapper 接口,CustomObjectWrapper 使得 MyBatis 能够对 CustomCollection 进行操作,如添加元素,而不必担心其他更复杂的属性操作。

使用场景

这种自定义包装器的一个潜在使用场景是在 MyBatis 的 ORM 映射中,当需要处理非标准集合类型时。例如,如果 CustomCollection 提供了一些特殊的行为或优化,那么使用 CustomObjectWrapper 可以确保这些特性能够在 MyBatis 操作中得到支持和利用。
总的来说,CustomObjectWrapper 提供了一个框架,用于将自定义集合类型集成到 MyBatis 中,尽管它需要进一步的实现来支持所有的 ObjectWrapper 接口方法。这种设计允许开发者根据特定需求定制 MyBatis 的行为,从而提高框架的灵活性和可用性。

MapWrapper类解读

这段代码定义了 类,它继承自 BaseWrapper 并实现了 ObjectWrapper 接口,专门用于处理 Java Map 类型的对象。MapWrapper 类提供了一系列操作,使得 MyBatis 能够通过反射机制访问和修改 Map 对象的内容。以下是对该类关键功能和方法的详细解释:

构造函数
  • MapWrapper(MetaObject metaObject, Map<String, Object> map):接受一个 MetaObject 和一个 Map<String, Object> 作为参数。metaObject 用于访问对象的元数据,map 是这个 MapWrapper 实例将要操作的 Map 对象。
方法实现
  • **get(PropertyTokenizer prop)**:如果属性名(prop)带有索引,则解析并返回集合中的相应元素;如果没有索引,则直接从 Map 中获取对应的值。
  • **set(PropertyTokenizer prop, Object value)**:如果属性名(prop)带有索引,则在集合中设置相应的元素;如果没有索引,则在 Map 中设置对应的值。
  • **findProperty(String name, boolean useCamelCaseMapping)**:直接返回属性名,因为 Map 的属性查找不需要转换或处理。
  • **getGetterNames()**** 和 ****getSetterNames()**:返回 Map 中所有键的数组,表示所有可用的getter和setter方法名。
  • **getSetterType(String name)**** 和 ****getGetterType(String name)**:返回给定属性名对应值的类型。如果 Map 中包含该属性,则返回其值的类类型;否则,默认为 Object 类型。
  • **hasSetter(String name)**hasGetter(String name)**:对于 Map 类型,理论上所有的属性名都可以有setter和getter,因为可以在 Map 中插入或查询任何键。
  • **instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory)**:用于动态创建并初始化一个属性值。在这里,它创建了一个新的 HashMap 并将其设置为指定属性的值。
  • **isCollection()**:返回 false,因为 Map 不被视为 Collection 类型。
  • **add(Object element)**** 和 ****addAll(List<E> element)**:抛出 UnsupportedOperationException,因为对于 Map 类型的数据结构,单独添加元素的操作没有定义。
设计意图

MapWrapper 类的设计主要是为了让 MyBatis 能够更灵活地处理 Map 类型的对象。通过实现 ObjectWrapper 接口,它提供了一种标准化的方式来访问和修改 Map 中的数据,无论是单个值还是集合类型的元素。这种封装使得 MyBatis 在执行 SQL 操作时,能够方便地映射结果集到 Map,或者将 Map 中的数据用作查询的参数。

使用场景

在 MyBatis 中,MapWrapper 被用来处理那些需要动态查询参数或希望将查询结果映射到 Map 对象的场景。通过 MapWrapper,用户可以灵活地操作 Map,无需担心如何将数据库的列映射到 Map 的键上。这在处理动态查询或构建动态响应时尤其有用。

BeanWrapper类解读

这段代码定义了 BeanWrapper 类,它是 MyBatis 反射模块中的一个关键部分,用于封装和操作 Java Bean 对象。BeanWrapper 继承自 BaseWrapper 并实现了 ObjectWrapper 接口,提供了一系列方法来访问和修改 Java Bean 的属性。以下是对该类关键功能和方法的详细解释:

构造函数
  • BeanWrapper(MetaObject metaObject, Object object):构造函数接收一个 MetaObject 和一个 Java 对象。MetaObject 是 MyBatis 反射模块的核心,用于访问对象的元数据,而 object 是被封装的 Java Bean 对象。metaClass 是通过 MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory()) 创建的,用于访问 Java Bean 的元数据,如属性的 getter 和 setter 方法。
主要方法
  • **get(PropertyTokenizer prop)**:根据 PropertyTokenizer 提供的属性名(可能包含索引或嵌套路径)获取属性值。如果属性名带有索引,则处理为集合或数组的访问;否则,通过 getBeanProperty 方法直接访问 Bean 的属性。
  • **set(PropertyTokenizer prop, Object value)**:根据 PropertyTokenizer 设置属性值。如果属性名带有索引,则处理为集合或数组的修改;否则,通过 setBeanProperty 方法修改 Bean 的属性。
  • **getBeanProperty**** 和 ****setBeanProperty**:这两个私有方法分别用于获取和设置 Java Bean 的属性值。它们通过 MetaClass 获取属性的 getter 或 setter Invoker,然后反射调用相应的方法。
  • 属性查找和类型获取方法:如 findPropertygetSetterTypegetGetterType 等方法,这些方法通过 MetaClass 实现,用于查找属性名、获取属性的类型等。
  • **hasSetter**** 和 ****hasGetter**:检查 Java Bean 是否有指定属性的 setter 或 getter 方法。
  • **instantiatePropertyValue**:如果需要,此方法用于实例化属性值。它通过 ObjectFactory 创建属性类型的新实例,并将其设置为属性值。
  • 集合操作相关方法isCollectionaddaddAll 方法在 BeanWrapper 中不支持,因为它封装的是单个 Java Bean 对象而非集合。
设计意图

BeanWrapper 的设计主要目的是封装对 Java Bean 对象的反射操作,提供统一的接口来访问和修改属性,支持嵌套属性和集合属性的操作。通过 MetaClass 和反射,BeanWrapper 能够动态地访问 Java Bean 的属性,这对于 MyBatis 在执行 SQL 语句时动态绑定参数值、映射查询结果到 Java 对象非常重要。

使用场景

在 MyBatis 的 ORM 映射过程中,BeanWrapper 被用于处理 Java Bean 类型的参数和结果对象。它允许 MyBatis 动态地访问和修改对象的属性,支持复杂的嵌套属性和集合属性的操作,从而使得 MyBatis 能够灵活地将数据库查询结果映射到 Java 对象,以及将 Java 对象作为查询参数。

CustomBeanWrapper类解读

类定义
  • CustomBeanWrapper 继承自 BeanWrapper 类,这意味着它继承了所有的公共方法和属性,使得可以覆盖(Override)或添加新的行为和功能。
构造函数
  • CustomBeanWrapper(MetaObject metaObject, Object object):构造函数接收两个参数:一个 MetaObject 和一个 Java 对象(object)。这与父类 BeanWrapper 的构造函数签名一致。在构造函数体中,它通过调用 super(metaObject, object) 将这些参数传递给父类的构造函数。
设计意图
  • 扩展 **BeanWrapper** 功能:虽然这个示例中 CustomBeanWrapper 没有实现任何新的功能或覆盖任何父类方法,实际使用时,你可以在这个类中添加新的方法或覆盖现有方法,以实现特定的行为或处理逻辑。例如,可以覆盖 getset 方法来添加日志记录、错误处理、属性值转换等自定义行为。
使用场景
  • 自定义对象包装器:如果你在使用 MyBatis 时有特殊需求,比如需要对某些 Java Bean 的属性访问或修改进行特殊处理,可以通过创建自定义的 BeanWrapper 来实现。这样可以保留 MyBatis 原有的映射和参数处理机制的同时,添加或修改特定的行为。
03-20 02:17