Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/346.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java JPA&;枚举表(又名“一个真正的查找表”) 问题_Java_Orm_Jpa 2.1 - Fatal编程技术网

Java JPA&;枚举表(又名“一个真正的查找表”) 问题

Java JPA&;枚举表(又名“一个真正的查找表”) 问题,java,orm,jpa-2.1,Java,Orm,Jpa 2.1,在缺少SQL枚举类型的情况下,很遗憾,似乎要创建一种比较流行的数据库设计模式(感谢您的链接,Nathan)。这些年来,我看到了许多不同的情况,但我目前正在努力解决的问题看起来是这样的: ID | ENUM |值 -----+-------------+---------- 1 |每周的第|天|周日 2 |每周的第|天|周一 ... 7 |每周第|天|周六 ... 18 |个人|类型|员工 19 |个人|型|经理 然后像这样使用-例如在人员表中: ID | NAME |类型 ----+----

在缺少SQL枚举类型的情况下,很遗憾,似乎要创建一种比较流行的数据库设计模式(感谢您的链接,Nathan)。这些年来,我看到了许多不同的情况,但我目前正在努力解决的问题看起来是这样的:

ID | ENUM |值
-----+-------------+----------
1 |每周的第|天|周日
2 |每周的第|天|周一
... 
7 |每周第|天|周六
...
18 |个人|类型|员工
19 |个人|型|经理
然后像这样使用-例如在人员表中:

ID | NAME |类型
----+----------+------
1 | Jane Doe | 19
这意味着Jane是经理,因为19是枚举表中person类型“manager”的主键

问题: 使用JPA(2.1),是否有一种优雅的方法将此构造映射到propper Java枚举

重要提示:我的“枚举表”有很多版本,它们的主键值不同,即“管理器”有时可能是第19行,但有时是第231行。但是,这些值在运行时不会更改。不幸的是,更改数据库模式也不是一个选项,但是使用任何JPA提供者的专有特性将是一个选项

什么有效 事实上,我找到了一个可行的解决方案,但这对我来说太难了:

public enum PersonType { EMPLOYEE, MANAGER }

@Table(name="PERSONS") @Entity public class Persons {
  @Id @Column(name="ID") long id;
  @Column(name="NAME") String name;
  @Convert(converter = PtConv.class) @Column(name="TYPE") PersonType type;
  // ...
}

@Converter public class PtConv implements AttributeConverter<PersonType, Integer> {
  // In a static initializer run a JDBC query to fill these maps:
  private static Map<Integer, PersonType> dbToJava;
  private static Map<PersonType, Integer> javaToDb;

  @Override public Integer convertToDatabaseColumn(PersonType attribute) {
    return javaToDb.get(attribute);
  }

  @Override public PersonType convertToEntityAttribute(Integer dbData) {
    return dbToJava.get(dbData);
  }
}
public enum PersonType{EMPLOYEE,MANAGER}
@表(name=“PERSONS”)@实体公共类人员{
@Id@Column(name=“Id”)长Id;
@列(name=“name”)字符串名称;
@Convert(converter=PtConv.class)@Column(name=“TYPE”)PersonType类型;
// ...
}
@转换器公共类PtConv实现AttributeConverter{
//在静态初始值设定项中,运行JDBC查询以填充这些映射:
私有静态映射dbToJava;
私有静态映射javaToDb;
@重写公共整数convertToDatabaseColumn(PersonType属性){
返回javaToDb.get(属性);
}
@重写公共PersonType convertToEntityAttribute(整数dbData){
返回dbToJava.get(dbData);
}
}

如果CDI在
@Converter
s中可用,我会接受这种情况,但静态构造测试是一场噩梦。

您可以使用老式方法:

public final class EnumMapping {
    ... // define & load singleton data
    public int getId(Enum<?> e) { 
        ... 
    }
    public <E extends Enum<E>> E valueOf(int id, Class<E> strictCheck) { 
        ... 
        return strictCheck.cast(result);
    }
}

@Table(name="PERSONS") @Entity public class Persons {
  @Id @Column(name="ID") long id;
  @Column(name="NAME") String name;
  @Column(name="TYPE") int typeId;

  public void setPersonType(PersonType type) { this.typeId = EnumMapping.getInstance().getId(type); }
  public PersonType getPersonType() { return EnumMapping.getInstance().valueOf(typeId, PersonType.class); }
}
公共最终类枚举映射{
…//定义并加载单例数据
公共int getId(枚举e){
... 
}
public E valueOf(int-id,Class-strictCheck){
... 
返回strictCheck.cast(结果);
}
}
@表(name=“PERSONS”)@实体公共类人员{
@Id@Column(name=“Id”)长Id;
@列(name=“name”)字符串名称;
@列(name=“TYPE”)int-typeId;
public void setPersonType(PersonType类型){this.typeId=EnumMapping.getInstance().getId(类型);}
public PersonType getPersonType(){返回EnumMapping.getInstance().valueOf(typeId,PersonType.class);}
}

因此,您可以(a)使用java枚举获取/设置值,以及(b)在您想要的特定时刻初始化映射。

作为参考,这就是我解决问题的方法。适当的Java枚举是我的首选,我会接受任何比这更好的答案

@Table(name="PERSONS") @Entity public class Persons {
  @Id @Column(name="ID") long id;
  @Column(name="NAME") String name;
  @Column(name="TYPE") BaseEnum type;   // known to be "PersonTypeEnum"

  public PersonType getType() {
    switch(type.getValue()) {
      case "EMPLOYEE": return PersonType.EMPLOYEE;
      case "MANAGER":  return PersonType.MANAGER;
    }
    throw new IllegalStateException(); 
  }

  public void setType(PersonTypeEnum type) {
    this.type = type;
  }
  // ...
}

@Entity @Inheritance @DiscriminatorColumn(name="ENUM") @Table(name="ENUMS")
public abstract class BaseEnum {
  @Id private int id;
  @Column(name="VALUE") String value;
  // ...
}

@Entity @DiscriminatorValue("PERSON_TYPE")
public class PersonTypeEnum extends BaseEnum { }

因此,枚举值的getter和setter有不同的类型,设置值需要对实体的引用,这会进一步破坏代码。

我不知道为什么您认为这有点流行。只需使用并保持您(以及未来维护人员)的生活简单即可。不幸的是,数据库模式不是我设计的。它的存在时间比Java应用程序长得多,并且安装在接受严格审核的客户机上。这就是为什么我写了
更改数据库模式不幸的是也不是一个选项
。您所描述的是“一个真正的查找表”,谢谢您的链接!很高兴知道,即使Joe Celko也同意这是一个糟糕的设计……JPA2.1将在本机上支持转换器。请浏览此链接()。我理解您的建议,这与使用
@Converter
进行测试一样困难。基本上,这将是非常相同的方法,只是避免注释?如果我误解了你的意思,你能详细说明一下吗?据我所知,你有两个不同的任务:(a)将int-id映射到enum值,反之亦然;(b)在实体中使用枚举,而不是int-id。您案例中的第一个任务可以通过从DB加载(或在测试期间生成)并初始化EnumMapping实例来解决。它可以是非最终的,并且为了测试目的而更改,非常简单。第二个任务-您应该有一些静态上下文中可用的映射,我建议使用singleton(最简单的方法)。是的,它类似于@Converter方法,但您在初始化方面有更多的自由。问题-我可能完全不同意-如果您有一个java
enum
,为什么还需要一个查找表?这是否意味着您需要冗余地维护两个数据集?如果您只有一个枚举,而没有数据库表来查找该枚举(拥有实体的列值除外),那么只需使用该值并在属性上使用注释即可。否则我认为你的解决方案是好的。我知道java枚举更易于使用,但每次1/22/2对数据库枚举集进行更改时都必须编译新代码,这很麻烦,有时会破坏交易。@coderatchet-是的,你完全正确。这是多余的。在这个问题中,我不能真正清楚地表达的是DB模式和内容超出了我的控制范围。看来JPA2.2将允许在转换器中进行注入,所以这将为我解决这个问题。