我希望使用Spring Data JPA管理人员,团队及其成员资格。 以下测试(whenAddingANewTeamTroughPerson)
entityManager.persist(alex);
personRepository.save(alex);
,为什么呢?在后一种情况下,我得到以下异常:
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: hello.data.model.Person; nested exception is java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: hello.data.model.Person
表现为CASCADE未正确设置。但它必须是正确的,因为它适用于entityManager。
PersonRepositoryTest:
@RunWith(SpringRunner.class)
@DataJpaTest
public class PersonRepositoryTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private PersonRepository personRepository;
@Autowired
private TeamRepository teamRepository;
@Test
public void whenFindOne_thenReturnPerson() {
// given
Person alex = new Person("alex");
entityManager.persist(alex);
entityManager.flush();
// when
Person found = personRepository.findOne(alex.getId());
// then
assertThat(found.getName(), equalToIgnoringWhiteSpace(alex.getName()));
}
@Test
public void whenAddingANewTeamTroughPerson_thenNoException() {
// given
Person alex = new Person("alex");
Team t = new Team("team");
alex.addTeam(t, "integrationTest", new Date());
personRepository.save(alex);
//entityManager.persist(alex);
// when
Person found = personRepository.findOne(alex.getId());
// then
assertThat(found.getTeams().size(), is(1));
}
}
PersonRepository:
public interface PersonRepository extends CrudRepository<Person, Integer> {}
人:
@Entity
public class Person {
private static final Logger LOG = LoggerFactory.getLogger(Person.class);
private final IntegerProperty id = new SimpleIntegerProperty(this, "id");
private final StringProperty name = new SimpleStringProperty(this, "name");
private Set<PersonTeam> personTeams = new LinkedHashSet<>();
private ListProperty<Team> teams = new SimpleListProperty<>(this, "teams", FXCollections.observableArrayList());
public Person(String s) {
this.name.set(s);
}
public Person() {}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
public Integer getId() {
return id.get();
}
public void setId(Integer id) {
this.id.set(id);
}
public IntegerProperty idProperty() {
return id;
}
@Basic
@Column(name = "name")
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
public StringProperty nameProperty() {
return name;
}
@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.person", cascade=CascadeType.ALL, orphanRemoval = true)
public Set<PersonTeam> getPersonTeams() {
return personTeams;
}
public void setPersonTeams(Set<PersonTeam> personTeams) {
this.personTeams = personTeams;
teams.setAll(personTeams.stream().map(PersonTeam::getTeam).collect(Collectors.toList()));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (getId() != null ? !getId().equals(person.getId()) : person.getId() != null) return false;
if (getName() != null ? !getName().equals(person.getName()) : person.getName() != null) return false;
return true;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (getName() != null ? getName().hashCode() : 0);
return result;
}
/**
* Returns an Observable List that must remain unchanged.
* Unfortunately there is no RO Observable List Interface to refer to.
*/
@Transient
public ObservableList<Team> getTeams() {
return teams.get();
}
@Transient
public ReadOnlyListProperty<Team> teamsProperty() {
return teams;
}
public void addTeam(Team team, String createdBy, Date createdDate) {
final PersonTeam personTeam = new PersonTeam(this, team);
personTeam.setCreatedBy(createdBy);
personTeam.setCreatedDate(createdDate);
if( !personTeams.add(personTeam) ) {
LOG.error("Failed to add personTeam " + personTeam + " to collection personTeams " + personTeams);
}
if( !team.getPersonTeams().add( personTeam ) ) {
LOG.error("Failed to add personTeam " + personTeam + " to collection team.getPersonTeams " + team.getPersonTeams());
}
teams.setAll(personTeams.stream().map(PersonTeam::getTeam).collect(Collectors.toList()));
}
public void removeTeam(Team team) {
PersonTeam personTeam = new PersonTeam( this, team );
team.getPersonTeams().remove( personTeam );
personTeams.removeIf( pt -> pt.getTeam() == team );
personTeam.setPerson( null );
personTeam.setTeam( null );
teams.setAll(personTeams.stream().map(PersonTeam::getTeam).collect(Collectors.toList()));
}
/*
The method is not intended to be used in the GUI.
Use the {@link GUIRepresentable} interface instead.
*/
@Override
public String toString() {
return name.getValue();
}
}
PersonTeam:
@Entity
@Table(name = "person_team")
@AssociationOverrides({
@AssociationOverride(name = "pk.person",
joinColumns = @JoinColumn(name = "PERSON_ID")),
@AssociationOverride(name = "pk.team",
joinColumns = @JoinColumn(name = "TEAM_ID")) })
public class PersonTeam implements java.io.Serializable {
private PersonTeamPk pk = new PersonTeamPk();
private Date createdDate;
private String createdBy;
public PersonTeam() {
}
public PersonTeam(Person person, Team team) {
this.pk.setPerson(person);
this.pk.setTeam(team);
}
@EmbeddedId
public PersonTeamPk getPk() {
return pk;
}
public void setPk(PersonTeamPk pk) {
this.pk = pk;
}
@Transient
public Person getPerson() {
return getPk().getPerson();
}
public void setPerson(Person person) {
getPk().setPerson(person);
}
@Transient
public Team getTeam() {
return getPk().getTeam();
}
public void setTeam(Team team) {
getPk().setTeam(team);
}
@Temporal(TemporalType.DATE)
@Column(name = "CREATED_DATE", nullable = false, length = 10)
public Date getCreatedDate() {
return this.createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
@Column(name = "CREATED_BY", nullable = false, length = 10)
public String getCreatedBy() {
return this.createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
PersonTeam that = (PersonTeam) o;
if (getPk() != null ? !getPk().equals(that.getPk())
: that.getPk() != null)
return false;
return true;
}
public int hashCode() {
return (getPk() != null ? getPk().hashCode() : 0);
}
}
PersonTeamPk:
@Embeddable
public class PersonTeamPk implements java.io.Serializable {
private Person person;
private Team team;
@ManyToOne(cascade = CascadeType.ALL)
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
@ManyToOne(cascade = CascadeType.ALL)
public Team getTeam() {
return team;
}
public void setTeam(Team team) {
this.team = team;
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PersonTeamPk that = (PersonTeamPk) o;
if (person != null ? !person.equals(that.person) : that.person != null) return false;
if (team != null ? !team.equals(that.team) : that.team != null)
return false;
return true;
}
public int hashCode() {
int result;
result = (person != null ? person.hashCode() : 0);
result = 31 * result + (team != null ? team.hashCode() : 0);
return result;
}
}
小组:
@Entity
public class Team implements Serializable, Comparable<Team> {
private final IntegerProperty id = new SimpleIntegerProperty(this, "id");
private final StringProperty name = new SimpleStringProperty(this, "name");
private Set<PersonTeam> personTeams = new LinkedHashSet<>();
public Team(String s) {
this.name.set(s);
}
public Team() {}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
public Integer getId() {
return id.get();
}
public void setId(Integer id) {
this.id.set(id);
}
public IntegerProperty idProperty() {
return id;
}
@Basic
@Column(name = "name")
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
public StringProperty nameProperty() {
return name;
}
@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.team", orphanRemoval = true, cascade = CascadeType.ALL)
public Set<PersonTeam> getPersonTeams() {
return personTeams;
}
public void setPersonTeams(Set<PersonTeam> personTeams) {
this.personTeams = personTeams;
}
public static Callback<Team, Observable[]> extractor() {
return (Team p) -> new Observable[]{p.nameProperty()};
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Team team = (Team) o;
if (getId() != null ? !getId().equals(team.getId()) : team.getId() != null) return false;
if (getName() != null ? !getName().equals(team.getName()) : team.getName() != null) return false;
return true;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (getName() != null ? getName().hashCode() : 0);
return result;
}
@Override
public String toString() {
return name.getValue();
}
@Override
public int compareTo(Team o) {
return Integer.compare(getId(), o.getId());
}
}