簡體   English   中英

Spring + Hibernate為什么不保持多對多關系?

[英]spring+hibernate why is many-to-many relationship not persisted?

我有一個實體E1定義為

@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);
    }

}

E1RepositoryE2Repository@Repository延伸注釋接口JpaRepository和具有空體)。

當我在調試器中逐步執行c方法時,我看到在最后兩行em.refresh行之后, e1e2的集合都被清除了。

根據我在Stack Overflow上發現的其他問題,我嘗試將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是由spring創建的實現JpaRepository的bean。 當運行此代碼時,休眠對象將為result對象運行一個插入語句(休眠將其所有sql命令輸出到控制台),而“ match_robot”表則不會運行。 我不應該在此代碼段之前將result為休眠狀態,以使其處於休眠狀態,而match處於持久狀態, robot處於持久狀態,並且resultmatchrobot屬性已設置為matchrobot分別。

基於Stack Overflow和其他網站上的其他問題,我還嘗試將matchs變量定義為

@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);
}

JpaRepository.save()不會將更改刷新到數據庫。 必須使用JpaRepository.flush()JpaRepository.saveAndFlush()刷新更改。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM