如何使用注释在Hibernate中映射地图列表?

时间:2015-02-09 21:51:42

标签: java hibernate java-ee jpa

我正在尝试使用Hibernate注释将地图列表映射到类中。

我希望我的最后一堂课看起来像这样:

@Entity
@Table(name = "PERSON")
public class Person {
    @Id
    @GeneratedValue
    private long id;

    @OneToMany(mappedBy = "person")
    private List<Map<String, Trip>> trips; 
}

旅行表有一个旅行号码,每次旅行都有多行,旅行的状态为。

@Entity
@Table(name = "TRIP")
public class Trip {
    @Id
    @GeneratedValue
    private long id;

    @Column(name = "PERSON_ID")
    private Person person;

    @Column(name = "TRIP_NO")
    private int trip_no;

    @Column(name = "STATE")
    private String state;

    ...
}

因此TRIP表包含以下列:

Name         Type
ID           NUMBER(9)
PERSON_ID    NUMBER(9)
TRIP_NO      LONG
STATE        VARCHAR2(16)
.
.
.

ID  PERSON_ID  TRIP_NO  STATE ...
1   1          1        MN    ...
2   1          1        WI    ...
3   1          2        ND    ...
4   1          2        MT    ...
5   2          1        IA    ...

因此在地图列表中,TRIP_NO是List的索引,STATE是Map的关键。第1人参加了2次旅行,第一次是去了明尼苏达州和威斯康星州,第二次去了北达科他州和蒙大拿州。第2人到爱荷华州进行了1次旅行。

我无法浏览以找到如何指定此配置。

第二个问题:没有JPA 2可以做到吗?

3 个答案:

答案 0 :(得分:1)

这是我的方法:

// composite primary key
public class TripId implements Serializable {
    private int id;
    private long trip_no;

    public TripId(int id, long trip_no) {
        this.id = id;
        this.trip_no = trip_no;
    }

    // getters, setters, hashCode, equals
}

编辑:我必须在以下评论中的Welshbard问题后更新实体模型。变化是:

  • Person实体:Map<String, Trip> trips;替换为List<Trip> trips;
  • Trip实体:String state;字段已添加为来自trips实体的Person密钥的替代
  • 用于填充数据/输出模式的构造函数/ setter /代码段已相应更新
@Entity
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
    private List<Trip> trips;

    public Person() {
        this.trips = new List<>();
    }

    public void setTrips(List<Trip> trips) {
        this.trips = trips;
    }

    ...
}
@Entity
@IdClass(TripId.class)
public class Trip {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @Id
    private long trip_no;

    private String state;

    @ManyToOne
    private Person person;

    public Trip() {

    }

    public Trip(String state, long trip_no, Person person) {
        this.state = state;
        this.trip_no = trip_no;
        this.person = person;
    }

    ...
}

我们现在可以使用上面的ORM并填充一些数据:

Person person1 = new Person();
Person person2 = new Person();

Trip mn1Trip1 = new Trip("MN", 1, person1);
Trip wi1Trip1 = new Trip("WI", 1, person1);
Trip nd2Trip1 = new Trip("ND", 2, person1);
Trip mt2Trip1 = new Trip("MT", 2, person1);
Trip ia1Trip2 = new Trip("IA", 1, person2);
// more than one trip by a person goes to the same state
Trip mn2Trip1 = new Trip("MN", 2, person1);
Trip ia2Trip2 = new Trip("IA", 2, person2);
Trip mn1Trip2 = new Trip("MN", 1, person2);

person1.setTrips(Arrays.asList(mn1Trip1, wi1Trip1, nd2Trip1, mt2Trip1, mn2Trip1));
person2.setTrips(Arrays.asList(ia1Trip2, ia2Trip2, mn1Trip2));

em.getTransaction().begin();
em.persist(person1);
em.persist(person2);
em.getTransaction().commit();

最后,我们可以查询数据库以查看模式以及如何填充数据:

Person table
============
id
--
 1
 7

Trip table
===========
id trip_no person_id state
-- ------- --------- -----
  9       2 IA            7
  4       2 ND            1
  8       1 IA            7
  6       2 MN            1
  2       1 MN            1
 10       1 MN            7
  3       1 WI            1
  5       2 MT            1

注意:ID不同,但架构已正确创建,数据按预期填充。

关于以下评论的问题:

// Person class have only trips associated with that person
String jpql1 = "SELECT p.trips FROM Person p WHERE p.id = 1";
List<Trip> trips = em.createQuery(jpql1, Trip.class)
                     .getResultList();

// the code can look at the Trips to find out the trip_no
String jpql2 = "SELECT t.trip_no FROM Trip t JOIN Person p " +
               "WHERE p.id = :id AND t.state = :state";
List<Long> trip_nos = em.createQuery(jpql2, Long.class)
                        .setParameter("id", 1)
                        .setParameter("state", "MN")
                        .getResultList();

关于第二个问题:

  

没有JPA 2可以完成吗?

您可以尝试使用普通的旧JDBC并使用java.sql.Statement插入SQL语句。

答案 1 :(得分:0)

我不确定,因为没有测试过,并且之前没有经历过完全相同的事情,但我认为使用List Map<K,V>的关联字段将是有点复杂,您可以在Many-to-One实体中为person提供Trip rel,然后在{person}实体为One-to-Many提供Trips

@Entity
@Table(name = "TRIP")
public class Trip {    
    /*
    code
    */
    @ManyToOne(cascade=CascadeType.ALL)
    private Person person;

    /*
    code
    */
}

并且Person实体不使用List<Map<String, Trip>>而不是使用 @MapKey 注释Map<String, Trip>

@Entity
@Table(name = "PERSON")
public class Person {
    @Id
    @GeneratedValue
    private long id;

    @OneToMany(mappedBy = "person")
    @MapKey(name = "state")
    private Map<String, Trip> trips; 
}

但是你需要复习一下,我希望这会有所帮助。

答案 2 :(得分:0)

除非您更改数据库架构,否则无法实现所需。请注意Hibernate文档如何将集合类型省略为集合的有效内容。

  

当然,Hibernate也允许持久化集合。这些   持久集合几乎可以包含任何其他Hibernate类型,   包括:基本类型,自定义类型,组件和引用   其他实体。   From Hibernate Collection Mapping

你不能用hibernate来做这件事,因为你的数据库设计很糟糕。基本上,您的TRIP表中包含太多信息,因为它存储了旅行和目的地。

理想情况下,您有旅行和目的地。

Trip包含id,person_id和trip_no,用于通过@OrderBy列表中的排序。

目的地包括id,trip_id,@MapKey使用的state_id以及目的地的其他信息。

Java代码就是这样的。没有检查是否有任何错误。只是为了给你一个想法。

@Entity
@Table(name = "PERSON")
public class Person {
    @Id
    @GeneratedValue
    private long id;

    @OneToMany(mappedBy = "person")
    @OrderBy(clause=trip_no)
    private List<Trip> trips; 
}

@Entity
@Table(name = "TRIP")
public class Trip {
    @Id
    @GeneratedValue
    private long id;

    @Column(name = "PERSON_ID")
    private Person person;

    @Column(name = "TRIP_NO")
    private int trip_no;

    @OneToMany(mappedBy = "trip")
    @MapKey(name = "state")
    private Map<String,Destination> destinations
}

@Entity
@Table(name = "DESTINATION")
public class Destination {

    private Trip trip;

    @Column(name = "STATE")
    private String state;

    ...
}