我有一個實體E1
定義爲spring + hibernate爲什麼多對多的關係不會持久?
@Entity
public class E1 {
@Id
@GeneratedValue
public long id;
@ManyToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(
name="e1_e2",
joinColumns = @JoinColumn(name = "e2_id"),
inverseJoinColumns = @JoinColumn(name = "e1_id")
)
public Set<E2> e2s = new HashSet<>();
}
E2定義爲
@Entity
public class E2 {
@Id
@GeneratedValue
public long id;
@ManyToMany(mappedBy = "e2s")
public Set<E1> e1s = new HashSet<>();
}
和控制器定義爲
實體@RestController
@RequestMapping("/")
public class C1 {
private final E1Repository e1Repository;
private final E2Repository e2Repository;
@PersistenceContext
EntityManager em;
@Autowired
public C1(E1Repository e1Repository, E2Repository e2Repository) {
this.e1Repository = e1Repository;
this.e2Repository = e2Repository;
}
@Transactional
@RequestMapping(method = POST)
public void c(){
E1 e1 = new E1();
E2 e2 = new E2();
e1Repository.save(e1);
e2Repository.save(e2);
em.refresh(e1);
em.refresh(e2);
e1.e2s.add(e2);
e2.e1s.add(e1);
e1Repository.save(e1);
e2Repository.save(e2);
em.refresh(e1);
em.refresh(e2);
}
}
(E1Repository
和E2Repository
是擴展JpaRepository
@Repository
註釋接口並有空的身體)。
,當我通過我的調試器c
方法步驟,我看到最後兩行em.refresh
後,無論是e1
和e2
有自己的電視機清除。
基於我對堆棧溢出發現的其他問題,我試圖定義E2爲
@Entity
public class E2 {
@Id
@GeneratedValue
public long id;
@ManyToMany(cascade = CascadeType.PERSIST)
@JoinTable(
name="e1_e2",
inverseJoinColumns = @JoinColumn(name = "e2_id"),
joinColumns = @JoinColumn(name = "e1_id")
)
public Set<E1> e1s = new HashSet<>();
}
但這並沒有幫助。
原始質詢(來自之前我試圖測試上面的簡化的情況) 我有一個類Robot
定義類似於以下:
@Entity
class Robot{
@Id
@GeneratedValue
private long id;
@ManyToMany(mappedBy="robots")
private Set<Match> matches = new HashSet<>();
}
和類Match
類似於
@Entity
class Robot{
@Id
@GeneratedValue
private long id;
@ManyToMany(cascade={CascadeType.Persist,CascadeType.Merge})
@JoinTable(
name = "match_robot",
joinColumns = {@JoinColumn(name = "Match_id")},
inverseJoinColumns = {@JoinColumn(name = "Robot_id")}
)
private Set<Robot> robots = new HashSet<>();
和類別Result
定義類似於
@Entity
public class Result {
@Id
@GeneratedValue
private long id;
@ManyToOne(optional = false)
private Match match;
@ManyToOne(optional = false)
@NotNull(groups = {Default.class, Creating.class})
private Robot robot;
}
並嘗試保存的關係如下:
resultRepository.save(result);
match.getRobots().add(result.getRobot());
result.getRobot().getMatches().add(match);
robotRepository.save(result.getRobot());
matchRepository.save(match);
entityManager.refresh(result);
entityManager.refresh(result.getRobot());
entityManager.refresh(match);
其中*Repository
是實現JpaRepository彈簧創建的Bean。 運行此代碼時,插入語句在result
對象(hibernate會將其所有sql命令輸出到控制檯)的休眠狀態下運行,但沒有一個用於「match_robot」表。我不應該說,這個段之前,result
是在Hibernate實體生命週期的過渡狀態,match
是在持久狀態,robot
是在持久狀態,result
的match
和robot
屬性已設置爲match
和robot
分別。
基於堆棧溢出和其他網站的其他問題,我也試圖定義匹配變量
@ManyToMany(cascade = CascadeType.PERSIST)
@JoinTable(
name = "match_robot",
inverseJoinColumns = {@JoinColumn(name = "Match_id")},
joinColumns = {@JoinColumn(name = "Robot_id")}
)
private Set<Match> matches = new HashSet<>();
這並沒有幫助。除此之外,我看到的唯一建議是確保在多方關係中兩個實體在堅持之前彼此保持一致,但據我所知,我在這裏這樣做。
我該如何堅持這種關係?
編輯:上下文完整的方法:
@Transactional
@RequestMapping(value = "/{match:[0-9]+}/results", method = RequestMethod.POST)
public ResultResource createResult(@PathVariable Match match,
@Validated(Result.Creating.class)
@RequestBody Result result) {
if (match == null) throw new ResourceNotFoundException();
result.setScorecard(scorecardRepository
.findById(result.getScorecard().getId()));
if (result.getScorecard() == null) {
throw new ScorecardDoesNotExistException();
}
result.setMatch(match);
//remove null scores
result.getScores().removeIf(
fieldResult -> fieldResult.getScore() == null
);
//replace transient robot with entity from database
Robot existingRobot = robotRepository
.findByNumberAndGame(result.getRobot().getNumber(),result.getRobot().getGame());
if (existingRobot == null) { //create new robot
//find team for this robot
Team existingTeam = teamRepository
.findByNumberAndGameType(
result.getRobot().getNumber(),
result.getScorecard().getGame().getType());
if (existingTeam == null) {
Team team = new Team();
team.setNumber(result.getRobot().getNumber());
team.setGameType(result.getMatch().getEvent().getGame().getType());
team.setDistrict(result.getMatch().getEvent().getDistrict());
teamRepository.save(team);
result.getRobot().setTeam(team);
}
else result.getRobot().setTeam(existingTeam);
result.getRobot().setGame(result.getMatch().getEvent().getGame());
robotRepository.save(result.getRobot());
entityManager.refresh(result.getRobot());
} else result.setRobot(existingRobot);
List<Robot> all = robotRepository.findAll();
//replace transient FieldSections with entities from database
//todo: reduce database hits
//noinspection ResultOfMethodCallIgnored
result.getScores().stream()
.peek(fieldResult -> fieldResult.setField(
fieldSectionRepository.findByIdAndScorecard(
fieldResult.getField().getId(),
result.getScorecard())))
.peek(fieldResult -> {
if (fieldResult.getField() == null)
throw new ScoresDoNotExistException();
})
.forEach(fieldResult->fieldResult.setResult(result));
if (!result.scoresMatchScorecardSections()) {
throw new ScoresDoNotMatchScorecardException();
}
if (!result.allMissingScoresAreOptional()) {
throw new RequiredScoresAbsentException();
}
if (!result.gameMatchesScorecard()) {
throw new GameDoesNotMatchScorecardException();
}
resultRepository.save(result);
match.getRobots().add(result.getRobot());
result.getRobot().getMatches().add(match);
robotRepository.save(result.getRobot());
matchRepository.save(match);
entityManager.refresh(result);
entityManager.refresh(result.getRobot());
entityManager.refresh(match);
return new ResultResourceAssembler().toResource(result);
}
您可以在調用'resultRepository.save(result)'之前發佈您的代碼嗎?'爲了讓您的對象網在開始保存時看起來像是什麼樣子?更好的整個方法?這個方法還是包含用@ Transactional註解的類? –
@ ansgar-schulte我遺漏了方法的前面部分,因爲除了創建和保存'Robot'對象並給出404(如果match對象爲null)之外,它沒有與'Robot'或「匹配」。我會繼續並添加它。 –
*當我在調試器中通過c方法時,我發現在最後兩行em.refresh行之後,e1和e2都清除了它們的集合。*:當然,它們是,因爲您刷新它們而沒有刷新你剛剛做出的改變。你爲什麼刷新?爲什麼你重新保存管理實體?所有這些重新保存和刷新都是無用的,甚至是適得其反的。閱讀http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#refresh(java.lang.Object):*刷新...覆蓋對實體所做的更改(如果有的話)。* 。 –