【问题标题】:JPA map an Embeddable class which contains a Map<Entity, Embeddable>JPA 映射一个包含 Map<Entity, Embeddable> 的 Embeddable 类
【发布时间】:2014-06-06 15:35:19
【问题描述】:

我正在使用 JPA 编写一个 Java EE 应用程序,它需要国际化。 为此,我创建了一个名为MultilingualString 的类,它将LanguageString 关联起来 (因此扩展HashMap&lt;Language, String&gt;)如下:

/**
 * This class is mapped to the database thanks
 * to the Locale.toLanguageTag() method
 * and Locale.fromLanguageTag(String) constructor
 */
public class Language {
    public Locale locale;

    /* Getters, Setters ... */  
}

/**
 * NB : This class extends HashMap to override the put method
 * which needs to ignore putting null values instead of throwing
 * a NullPointerException
 */
public class MultilingualString extends HashMap<Language, String> {
    public Map<Language, String> getStrings() {
        return (this);
    }

    /* ... */
}

现在,假设我们有一个 Entity,它的名称取决于 Language

@Entity
@Access(AccessType.PROPERTY)
public class Entity {
    private Long id;
    private MultilingualString name;

    @Id
    public Long getId() { ... }

    @Transient
    public MultilingualString getName() { ... }

    @ElementCollection
    @MapKeyJoinColumn(name = "language_id")
    public Map<Language, String> getStrings() {
        return (this.name.getStrings());
    }

    /* ... */
}

此代码生成以下数据库架构:

Language        :
    id          | bigint                        | NOT NULL  | primary key
    languageTag | character varying(255)        |           |
---------------------------------------------------------------------------------------------------
Entity          :
    id          | bigint                        | NOT NULL  | primary key
---------------------------------------------------------------------------------------------------
Entity_strings  :
    entity_id   | bigint                        |           | foreign key references Entity(id)
    language_id | bigint                        |           | foreign key references Language(id)
    string      | character varying(255)        |           |

这几乎是好的。

这个实现的问题是@TransientMultilingualString 字段上的重复 和 @ElementCollection @MapKeyJoinColumngetStrings() 方法上(你必须每次都重写 您需要在数据库中映射一个MultilingualString BTW:每个实体类至少 1 个)。 在我看来,它使代码变得丑陋且难以维护。

另一件事是:Entity 通过MapLanguageString 相关联。 应该有一个将它们关联起来的类:当我阅读时说LocalizedString here

所以我希望MultilingualString 表现得像一个值类型,所以 我不需要重复上面提到的那些多个注释并实现LocalizedString

以下是我到目前为止的想法。它不起作用,GlassFish 无法启动 有了这样的映射,当 EclipseLink 看到 MultilingualString 映射时,我总是得到一个 java.util.NoSuchElementException

@Embeddable
public class LocalizedString {
    @ManyToOne
    private Language language;
    private String string;
}

@Embeddable
public class MultilingualString extends HashMap<Language, LocalizedString> {

    /**
     * Here I lost myself in all the available annotations
     * and tried thousands of permutation, but always the same exception
     * @MapKeyJoinColumn ?
     * @MapKeyClass ?
     * @CollectionTable ?
     * ...
     */
    @ElementCollection
    public Map<Language, LocalizedString> getStrings() {
        return (this);
    }
}

@Entity
public class Entity {
    @Id
    private Long id;
    @Embedded
    private MultilingualString name;
}

因此,我希望通过此实现获得与上面所写相同(或最接近的)数据库映射。 这甚至可能吗?我读到Embeddable 类不应该包含另一个(集合)Embeddable 类,但在这里,我就是这种情况。

看了几篇文章和博客,并没有发现这么多像这样的情况(“JPA Map with Entity key and Embeddable value”)

实际上,我正在尝试做类似于this 的事情 除了我的地图值是Embeddable 类。 我找到了ONE similar situation (除了我希望他的 Root 类是 Embeddable)在 StackOverflow 上,我测试了一个答案(没有运气),但它没有“标记为答案”并且问题没有太大成功.

【问题讨论】:

  • 我认为不应修改 Entity 类以包含有关 i18n 的任何信息。您只需要在实体中保留键,并根据语言环境为其获取本地化消息并通过...据我所知,您正在将本地化内容持久化到数据库中?
  • @Vach 是的,但我需要:表单邀请用户添加一些 Entity 实例,此表单询问用户名称是数据库中可用的语言。这些名称不是静态的。当然,对于静态标签,我不会将它们存储在数据库中,我有一些属性文件(messages_en、messages_fr 等),但我不能将它们用于此目的,因为我不知道字符串是什么会的。
  • 好的,我有过这样的经历,这就是我所做的。我和你的情况几乎一样,用户应该输入本地化信息。起初我(就像你用实体存储它一样)那是可怕的。然后我们为定位信息保留了单独的实体,该实体保留了定位特定实体和与其相关的所有定位所需的信息。目标实体对其本地化一无所知。然后我们写了一个切面,这个切面把所有我们感兴趣的方法都返回本地化的值,并在执行后注入本地化的内容......
  • 因此,使用特定语言环境登录的用户可以获得准确的本地化信息(如果没有可用的,则默认为英语)。我们缓存了这些方面的执行,一切都运行良好......我的观点是本地化是 AOP 的典型任务的“交叉关注”,尝试通过 OOP 实现它只会损害您的性能和代码可读性...... AOP 是一个真正必须了解的可编译语言(如 java)的技术,一旦你使用它,你会想知道到目前为止没有它你是怎么做的......
  • 如果你需要,我可以给你那个方面的代码。您不仅可以通过这种方式解决本地化问题,还包括其安全性、事务管理、日志记录等......

标签: java jakarta-ee jpa orm eclipselink


【解决方案1】:

这是可以处理实体本地化的方面示例:

@Aspect
public class LocalizationAspect {

    @AfterReturning(
            pointcut = "@within(MyManager) && execution(MyEntity methodThatReturnsEntity(..))",
            returning = "entity"
    )
    public void localizeMyEntity(MyEntity entity) {
        localize(entity);
    }
}

只需稍作更改,您就可以将其应用于返回实体的集合...

localie() 在返回结果之前使用实体 ID 导航并将本地化信息注入该实体。

【讨论】:

    猜你喜欢
    • 2012-08-12
    • 2012-09-25
    • 2020-06-05
    • 2011-07-12
    • 1970-01-01
    • 2016-07-05
    • 2020-05-06
    • 2015-05-30
    • 2012-04-22
    相关资源
    最近更新 更多