【问题标题】:Approaches for populating Java objects from maps从地图中填充 Java 对象的方法
【发布时间】:2015-05-11 14:05:47
【问题描述】:

我正在处理工作中反复出现的模式。

public class SapExample
{

  public static void main(String[] args)
  {
    // read SAP data..

    // get table
    List<Map<String, Object>> objects = getTable("ET_WIDGET");

    for (Map<String, Object> entry : objects)
    {
      Object value = entry.get("WDGT_ID");
      String id = (value != null) ? String.valueOf(value) : ""; // or null

      value = entry.get("WDGT_NAME");
      String name = (value != null) ? String.valueOf(value) : ""; // or null

      value = entry.get("WDGT_DESC");
      String description = (value != null) ? String.valueOf(value) : ""; // or null

      // create object from data
      //Widget obj = new Widget(id, name, description);
    }
  }

  private static List<Map<String, Object>> getTable(String string)
  {
    return Collections.emptyList();
  }
}

简单的例子,对象之间没有关系。更复杂的场景,其中 A 类有 B 类的成员也确实会发生,并且可以通过使用外键来解决。

从数据库返回的表 被处理为List&lt;Map&lt;String, Object&gt;&gt;,其中Map&lt;String, Object&gt; 描述了给定表中的一行,该行将列名映射到Object 类型的值,这些值在内部主要是StringDate 类型。

映射包含初始化某种类型的对象所需的数据,这取决于当前表。我无法改变这一点。

程序员以我上面展示的方式直接或间接访问数据并创建所需对象的实例。


我发现这是一种非常冗长且不美观的编程方式。

是否可以利用 JAXB 或其他技术以声明方式(例如通过使用 XML)解决此问题? 如果可以使用 JAXB,我该怎么办?我想我必须重写一些类。

【问题讨论】:

  • 您的数据是否存储为 XML?如果是这样,那么是的,您可以使用 XML 解析器,如 DOM 或 SAX 或 JAXB。
  • 数据存储为List&lt;Map&lt;String, Object&gt;&gt;,正如我在问题中提到的那样。
  • 不清楚你问的是什么。 JAXB 专门用于 XML 绑定 (JavaXML)。我在您的问题中没有看到任何 XML,所以我不明白您为什么提到这一点。您的问题是关于 1) 数据库 Java 对象映射吗? 2) 初始化默认值? 3) 序列化?
  • 你不能简单地使用 Hibernate 或其他 ORM 吗?
  • 它是关于以声明方式(例如通过使用 XML)的数据库映射 (SAP)。我正在考虑创建一个可以以声明方式描述的对象模型,并帮助我以List&lt;Map&lt;String, Object&gt;&gt; 的形式从表中创建对象。

标签: java xml serialization jaxb deserialization


【解决方案1】:

第一件事 - 忘记 XML,因为它与你想做的事情没有什么特别的关系。

您想要的是在Map 中获取数据并将其复制到普通对象中。在 Apache BeanUtils 库中有一个类可以做到这一点 - org.apache.commons.beanutils.BeanMap

有一些先决条件:您的目标类必须是 bean 类型的对象 - 换句话说,它们必须具有 putlic getter 和 setter。

然后您可以将其中一个包装在 BeanMap 中,如下所示:

BeanMap beanMap = new BeanMap(myObject)

然后设置对象中的字段:

beanMap.put(fieldName, value)

因此您可以看到您可以使用输入映射中条目的列名来设置目标对象中的字段。问题是列名与字段名不完全相同,因此您必须具有某种映射功能。我建议您通过在 Java 中创建映射来做到这一点:

someMap.put("ET_WIDGET.WIDGET_ID", "ID")

或者你使用一个属性文件。

您可以使用Class.newInstance 方法创建对象本身,并以与上述类似的方式将表名映射到类名。

【讨论】:

  • 这看起来很有趣。 ...XML 可用于映射数据。
  • 如果您真的想将 XML 用于某事并且您不在乎什么(不用担心 - 您并不孤单),是的,您可以,但可能需要更长的时间而不仅仅是使用属性文件。
  • 感谢您的回答。正如@superbob 所说,使用 JPA 或其他一些 OR 映射器确实会是一种更简洁、更清晰的方法。但如果我必须编写自己的解决方案,我会在这里考虑所有答案。
【解决方案2】:

JAXB 似乎不适合您的情况,因为它旨在处理 XML,而您的问题不涉及 XML。

作为替代方案,您可以使用注释和反射:

创建自己的注释:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Mapped {
    String value() default "";
    String defaultValue() default "";
}

注释您的小部件/POJO:

public class Widget {
    @Mapped(key = "WDGT_ID", defaultValue = "")
    private String id;
    @Mapped(key = "WDGT_NAME", defaultValue = "")
    private String name;
    @Mapped(key = "WDGT_DESC", defaultValue = "")
    private String description;

    [constructors...]
    [getters&setters...]
}

实现一个可以创建 POJO 的“factory/builder”方法:

public T getObject(Map<String, Object> source, Class<T> clazz) {
    [some java introspection code]
}

您的代码将变为:

public class SapExample
{

  public static void main(String[] args)
  {
    // read SAP data..

    // get table
    List<Map<String, Object>> objects = getTable("ET_WIDGET");

    for (Map<String, Object> entry : objects) {
        Widget obj = getObject(entry , Widget.class);
    }
  }

  private static List<Map<String, Object>> getTable(String string)
  {
    return Collections.emptyList();
  }
}

这里的困难部分是“工厂”方法。我不会在这里提供代码,因为它需要大量时间来生成,并且实现可能取决于您的上下文。但我希望你已经理解了这个想法。

【讨论】:

  • 你将如何处理类之间的依赖关系,即如果Widget 有一个Foo 类型的成员,其数据驻留在表ET_FOO 中?
  • 选择:1) 什么都不做,单独使用 id。 2) 在getObject 方法中实现它,但老实说 3) 使用 JPA(任何基于 ORM 的解决方案)。我了解您可能不允许这样做,但在 2 中这样做可能会导致您自己(重新)实现 JPA。
猜你喜欢
  • 2020-03-21
  • 1970-01-01
  • 1970-01-01
  • 2015-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-03
相关资源
最近更新 更多