EclipseLinkを使用してJPA2にテンポラルテーブルを実装する方法を知りたいです。時間的とは、有効期間を定義するテーブルを意味します。
私が直面している問題の1つは、参照テーブルの性質上、主キーに有効期間が含まれているため、参照テーブルが参照テーブル(一時テーブル)に対する外部キー制約を持たなくなることです。
- エンティティの関係をどのようにマッピングしますか?
- それは、私のエンティティがそれらの有効な時間のエンティティとの関係をもはや持つことができないことを意味しますか?
- これらの関係を初期化する責任は、ある種のサービスまたは特殊なDAOで手動で行う必要がありますか?
私が見つけた唯一のものは、これを処理するDAOFusionと呼ばれるフレームワークです。
- これを解決する他の方法はありますか?
- このトピックに関する例またはリソース(時制データベースを備えたJPA)を提供していただけますか?
これは、データモデルとそのクラスの架空の例です。これは、時間的な側面を扱う必要のない単純なモデルとして始まります。
最初のシナリオ:非時間モデル
データモデル:
チーム:
@Entity
public class Team implements Serializable {
private Long id;
private String name;
private Integer wins = 0;
private Integer losses = 0;
private Integer draws = 0;
private List<Player> players = new ArrayList<Player>();
public Team() {
}
public Team(String name) {
this.name = name;
}
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQTEAMID")
@SequenceGenerator(name="SEQTEAMID", sequenceName="SEQTEAMID", allocationSize=1)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column(unique=true, nullable=false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getWins() {
return wins;
}
public void setWins(Integer wins) {
this.wins = wins;
}
public Integer getLosses() {
return losses;
}
public void setLosses(Integer losses) {
this.losses = losses;
}
public Integer getDraws() {
return draws;
}
public void setDraws(Integer draws) {
this.draws = draws;
}
@OneToMany(mappedBy="team", cascade=CascadeType.ALL)
public List<Player> getPlayers() {
return players;
}
public void setPlayers(List<Player> players) {
this.players = players;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Team other = (Team) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
プレイヤー:
@Entity
@Table(uniqueConstraints={@UniqueConstraint(columnNames={"team_id","number"})})
public class Player implements Serializable {
private Long id;
private Team team;
private Integer number;
private String name;
public Player() {
}
public Player(Team team, Integer number) {
this.team = team;
this.number = number;
}
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQPLAYERID")
@SequenceGenerator(name="SEQPLAYERID", sequenceName="SEQPLAYERID", allocationSize=1)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@ManyToOne
@JoinColumn(nullable=false)
public Team getTeam() {
return team;
}
public void setTeam(Team team) {
this.team = team;
}
@Column(nullable=false)
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
@Column(unique=true, nullable=false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((number == null) ? 0 : number.hashCode());
result = prime * result + ((team == null) ? 0 : team.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Player other = (Player) obj;
if (number == null) {
if (other.number != null)
return false;
} else if (!number.equals(other.number))
return false;
if (team == null) {
if (other.team != null)
return false;
} else if (!team.equals(other.team))
return false;
return true;
}
}
テストクラス:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/META-INF/application-context-root.xml"})
@Transactional
public class TestingDao {
@PersistenceContext
private EntityManager entityManager;
private Team team;
@Before
public void setUp() {
team = new Team();
team.setName("The Goods");
team.setLosses(0);
team.setWins(0);
team.setDraws(0);
Player player = new Player();
player.setTeam(team);
player.setNumber(1);
player.setName("Alfredo");
team.getPlayers().add(player);
player = new Player();
player.setTeam(team);
player.setNumber(2);
player.setName("Jorge");
team.getPlayers().add(player);
entityManager.persist(team);
entityManager.flush();
}
@Test
public void testPersistence() {
String strQuery = "select t from Team t where t.name = :name";
TypedQuery<Team> query = entityManager.createQuery(strQuery, Team.class);
query.setParameter("name", team.getName());
Team persistedTeam = query.getSingleResult();
assertEquals(2, persistedTeam.getPlayers().size());
//Change the player number
Player p = null;
for (Player player : persistedTeam.getPlayers()) {
if (player.getName().equals("Alfredo")) {
p = player;
break;
}
}
p.setNumber(10);
}
}
ここで、チームとプレーヤーが特定の時点でどのようにいたかを履歴に残すように求められます。そのため、追跡するテーブルごとに期間を追加する必要があります。それでは、これらの時間列を追加しましょう。から始めPlayer
ます。
2番目のシナリオ:時間モデル
データ・モデル:
ご覧のとおり、主キーを削除して、日付(期間)を含む別のキーを定義する必要がありました。また、テーブル内で繰り返すことができるようになったため、一意の制約を削除する必要がありました。これで、テーブルに現在のエントリと履歴を含めることができます。
Teamを一時的にする必要がある場合、状況はかなり醜くなります。この場合、Player
テーブルが必要とする外部キー制約を削除する必要がありますTeam
。問題は、JavaとJPAでそれをどのようにモデル化するかです。
IDは代理キーであることに注意してください。ただし、サロゲートキーには日付を含める必要があります。含めない場合、同じエンティティの複数の「バージョン」を(タイムライン中に)保存することはできません。