【问题标题】:Persisting a List of Integers with JPA?使用 JPA 持久化整数列表?
【发布时间】:2010-03-23 15:51:59
【问题描述】:

我们有一个 pojo,它需要一个整数列表。例如,我创建了一个Message pojo,并希望关联一个groupIds 列表(这些id 需要在UI 中查询和显示)。所以理想情况下,我们希望能够做这样的事情:

Message msg = em.find(Message.class, 101);
List<Integer> groupIds = msg.getGroupIds();

我的印象是这只需要一个带有 JPA 的 pojo,但根据 discussion here,我需要创建第二个 pojo,因为 JPA 是根据对象而不是原始类型工作的。

从该讨论中,我尝试了以下示例代码,但收到错误 openjpa-1.2.3-SNAPSHOT-r422266:907835 fatal user error: org.apache.openjpa.util.MetaDataException: The type of field "pojo.Group.messageId" isn't supported by declared persistence strategy "ManyToOne". Please choose a different strategy.

DDL:

创建表 "APP"."MESSAGE" (
  "MESSAGE_ID" INTEGER NOT NULL 始终作为标识生成(从 1 开始,按 1 递增),
  “作者” CHAR(20) 非空
 );

ALTER TABLE "APP"."MESSAGE" 添加约束 "MESSAGE_PK" PRIMARY KEY ("MESSAGE_ID");

创建表“APP”。“GROUP_ASSOC”(
  "GROUP_ID" INTEGER NOT NULL,
  “MESSAGE_ID”整数非空
 );

ALTER TABLE "APP"."GROUP_ASSOC" 添加约束 "GROUP_ASSOC_PK" PRIMARY KEY ("MESSAGE_ID", "GROUP_ID");

ALTER TABLE "APP"."GROUP_ASSOC" 添加约束 "GROUP_ASSOC_FK" FOREIGN KEY ("MESSAGE_ID")
 REFERENCES "APP"."MESSAGE" ("MESSAGE_ID");

POJO:

@Entity
@Table(name = "MESSAGE")
public class Message {
    @Id
    @Column(name = "MESSAGE_ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)    
    private Long messageId;

    @OneToMany   
    private List<Group> groups = new ArrayList<Group>();

    @Column(name = "AUTHOR")
    private String author;

    // getters/setters ommitted
}    

@Entity
@IdClass(pojo.Group.GroupKey.class)
@Table(name = "GROUP_ASSOC")
public class Group {

 @Id
 @Column(name = "GROUP_ID")
 private Long groupId;

 @Id
 @Column(name = "MESSAGE_ID")
 @ManyToOne
 private Long messageId;

 public static class GroupKey {
  public Long groupId;
  public Long messageId;

  public boolean equals(Object obj) {
   if(obj == this) return true;
            if(!(obj instanceof Group)) return false;
   Group g = (Group) obj;
   return g.getGroupId() == groupId && g.getMessageId() == messageId; 
  }

  public int hashCode() {
            return ((groupId == null) ? 0 : groupId.hashCode())
                ^ ((messageId == null) ? 0 : messageId.hashCode());
  } 
 }

 // getters/setters ommitted 
}

测试代码:

EntityManager em = Persistence.createEntityManagerFactory("JPATest").createEntityManager();
em.getTransaction().begin();

Message msg = new Message();
msg.setAuthor("Paul");
em.persist(msg);
List<Group> groups = new ArrayList<Group>();

Group g1 = new Group();
g1.setMessageId(msg.getMessageId());
Group g2 = new Group();
g2.setMessageId(msg.getMessageId());

msg.setGroups(groups);
em.getTransaction().commit();

这一切看起来很荒谬——用 3 个类(如果包含 GroupKey 复合标识类)来建模整数列表——难道没有更优雅的解决方案吗?

【问题讨论】:

    标签: jpa openjpa


    【解决方案1】:

    这是一个老话题,但自 OpenJPA2 以来情况发生了变化,现在您可以直接保留原始类型或字符串对象。使用 ElementCollection 注解使用简单的一对多链接,无需中间对象或链接表。这就是我们大多数人可能创建 SQL 模式的方式。

    @Entity @Table(name="user") @Access(AccessType.FIELD)
    public class User {
        @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
        private long id;    // primary key (autogen surrogate)
        private String name;
    
        // ElementCollection provides simple OneToMany linking.
        // joinColumn.name=foreign key in child table. Column.name=value in child table
        @ElementCollection(fetch=FetchType.LAZY)
        @CollectionTable(name="user_role", joinColumns={@JoinColumn(name="user_id")})
        @Column(name="role")
        private List<String> roles;
    
        public long getId() { return id; }
        public void setId(long id) { this.id = id; }
    
        public String getName() { return name; }
        public void setName(String name) { this.name=name; }
    
        public List<String> getRoles() { return roles; }
        public void setRoles(List<String> roles) { this.roles=roles; }
    
    }
    - - -
    CREATE TABLE user (
      id bigint NOT NULL auto_increment,
      name varchar(64) NOT NULL default '',
      PRIMARY KEY (id),
      UNIQUE KEY USERNAME (name)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
    
    CREATE TABLE user_role (
      user_id bigint NOT NULL,
      role varchar(64) NOT NULL default '',
      PRIMARY KEY (user_id, role)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
    

    【讨论】:

      【解决方案2】:

      我真的认为你所拥有的实际上是两个实体之间的多对多关联(我们称它们为MessageGroup)。

      表示这个的 DDL 是:

      CREATE TABLE "APP"."MESSAGE" (
        "MESSAGE_ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
        "AUTHOR" CHAR(20) NOT NULL
       );
      
      ALTER TABLE "APP"."MESSAGE" ADD CONSTRAINT "MESSAGE_PK" PRIMARY KEY ("MESSAGE_ID");
      
      CREATE TABLE "APP"."GROUP" (
        "GROUP_ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1)
       );
      
      ALTER TABLE "APP"."GROUP" ADD CONSTRAINT "GROUP_PK" PRIMARY KEY ("GROUP_ID");
      
      CREATE TABLE "APP"."MESSAGE_GROUP" (
        "GROUP_ID" INTEGER NOT NULL,
        "MESSAGE_ID" INTEGER NOT NULL
       );
      
      ALTER TABLE "APP"."MESSAGE_GROUP" ADD CONSTRAINT "MESSAGE_GROUP_PK" PRIMARY KEY ("MESSAGE_ID", "GROUP_ID");
      
      ALTER TABLE "APP"."MESSAGE_GROUP" ADD CONSTRAINT "MESSAGE_GROUP_FK1" FOREIGN KEY ("MESSAGE_ID")
       REFERENCES "APP"."MESSAGE" ("MESSAGE_ID");
      
      ALTER TABLE "APP"."MESSAGE_GROUP" ADD CONSTRAINT "MESSAGE_GROUP_FK2" FOREIGN KEY ("GROUP_ID")
       REFERENCES "APP"."MESSAGE" ("GROUP_ID");
      

      以及带注释的类:

      @Entity
      public class Message {
          @Id
          @Column(name = "MESSAGE_ID")
          @GeneratedValue(strategy = GenerationType.IDENTITY)    
          private Long messageId;
      
          @ManyToMany
          @JoinTable(
              name = "MESSAGE_GROUP", 
              joinColumns = @JoinColumn(name = "MESSAGE_ID"), 
              inverseJoinColumns = @JoinColumn(name = "GROUP_ID")
          ) 
          private List<Group> groups = new ArrayList<Group>();
      
          private String author;
      
          //...
      }    
      
      @Entity
      public class Group {    
          @Id
          @GeneratedValue
          @Column(name = "GROUP_ID")
          private Long groupId;
      
          @ManyToMany(mappedBy = "groups")
          private List<Message> messages = new ArrayList<Message>();
      
          //...
      }
      

      我不确定您是否需要双向关联。但是如果你想使用 JPA,你肯定需要开始考虑对象(在你的例子中,你仍然在设置 ids,你应该设置实体)。或者,也许 JPA 不是您所需要的。


      没有更优雅的解决方案吗?

      我不确定“优雅”是否合适,但 JPA 2.0 定义了 ElementCollection mapping(正如我在之前的回答中所说):

      它旨在处理几个非标准的关系映射。 ElementCollection 可用于定义与Embeddable 对象或Basic 值(例如字符串集合)的一对多关系。

      但那是在 JPA 2.0 中。在 JPA 1.0 中,如果您的提供者确实提供了这样的扩展,则您必须使用提供者特定的等效项。看来 OpenJPA 使用 @PersistentCollection

      【讨论】:

      • 不幸的是,我无法控制最终的模式(这当然是我用来学习的测试模式)。我无法添加第三个GROUP 表。我只有MESSAGEGROUP_ASSOC 表。这种方式还有可能吗?
      【解决方案3】:

      根据您的架构,您在组和消息之间存在多对一关系。这意味着单个消息可以属于多个组,但每个组可以有单个消息。

      实体看起来像这样。

      @Entity
      @Table(name = "GROUP_ASSOC")
      public class Group {
          @Id
          @Column(name="GROUP_ID")
          private int id;
      
          @ManyToOne
          @Column(name="MESSAGE_ID")
          @ForeignKey
          private Message message;
      
          // . . . 
      }
      
      @Entity
      public class Message {
          @Id
          @GeneratedValue(strategy=GenerationType.IDENTITY)
          @Column(name = "MESSAGE_ID")
          private int id;
      
          @Column(length=20)
          private String author;
      
          @OneToMany(mappedBy="message")  
          private Collection<Group> groups;
      }
      

      您的应用中不需要 IDClass(如果您的 ID 包含多列,则只需要一个)。

      要获取给定消息的 groupId,您可以编写这样的查询

          Query q =  em.createQuery("Select g.id from Group g where g.message.id = :messageId");
          q.setParameter("messageId", 1);
      
          List results = q.getResultList();
      

      或者只是迭代 Message.getGroups() :

      Message m = em.find(Message.class, 1);
      for(Group g : m.getGroups()) {
          // create a list, process the group whatever fits.
      }
      

      【讨论】:

        猜你喜欢
        • 2011-10-06
        • 1970-01-01
        • 1970-01-01
        • 2013-01-02
        • 2013-11-12
        • 1970-01-01
        • 2011-12-01
        • 2019-07-17
        • 2013-02-25
        相关资源
        最近更新 更多