简体   繁体   中英

Why does Hibernate with H2 not maintain bi-directional relations?

The Question

Why does Hibernate with H2 behave differently from Hibernate with MySQL?

We are using Hibernate as ORM. In our live system, we use a MySQL database and LiquiBase to maintain the db schema. In our test system, we use an in-memory H2 database directly (no Liquibase needed).

In our live system, bi-directional relations between entities are automatically updated by hibernate, when we update one side of the relationship (usually the one containing the foreign key). In our test environment, this kind of magic is gone and we are forced to update relations explicitly on both sides.

We don't understand, why our test environment behaves so differently. Is it simply a difference between MySQL+Liquibase versus H2?

I am aware, that most documentation for hibernate / JPA says that relations between objects still have to be maintained manually, even when using hibernate. But since it works in our live system, we would like to replicate the same behaviour for our tests.

We already tried to add more Annotations to give the H2 DB more information about the schema and its dependencies but that didn't help.

So, is there anything we can do to force these automatic updates in H2?

The Code

We have Users which can have CompanyPositions. This relation is modeled as a separate class: UserCompanyPosition.

Here is some code which I tried to strip down to the relevant parts. First the two entities and the relation class. Then a method where the relation is updated and small test method.

Please let me know which additional information would be helpful to answer this question.

User.java:

@Entity
@Cacheable
@org.hibernate.annotations.Cache(
    usage = CacheConcurrencyStrategy.READ_WRITE, region = "our_region")
@Table(name = "users")
public class User extends Model {

  @Id
  @Column(name = "id")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @OneToMany(mappedBy = "user")  // the relevant field
  private List<UserCompanyPosition> userCompanyPositions = new ArrayList<>();

  // ... default getters & setters, simple constructor
}

CompanyPosition.java:

@Entity
@Cacheable
@org.hibernate.annotations.Cache(
    usage = CacheConcurrencyStrategy.READ_WRITE, region = "our_region")
@Table(name = "company_positions")
public class CompanyPosition extends Model {

  @Id
  @Column(name = "id")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @OneToMany(mappedBy = "companyPosition") // relevant field
  private List<UserCompanyPosition> userCompanyPositions = new ArrayList<>();

  // ... default getters & setters, simple constructor
}

UserCompanyPosition.java:

@Entity
@Cacheable
@org.hibernate.annotations.Cache(
    usage = CacheConcurrencyStrategy.READ_WRITE, region = "our_region")
@Table(
    name = "users_company_positions",
    uniqueConstraints = {
@UniqueConstraint(columnNames = {"company_position_id", "user_id"})})
public class UserCompanyPosition extends Model {

  @Id
  @Column(name = "id")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "company_position_id", nullable = false)
  private CompanyPosition companyPosition;

  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "user_id", nullable = false)
  private User user;

  public UserCompanyPosition(User user, CompanyPosition companyPosition) {
    this.user = user;
    this.companyPosition = companyPosition;
  }

//... default getters & setters
}

The creation method in UserCompanyPositionController.java:

protected Model createUserCompanyPosition(User user, CompanyPosition companyPosition) {
    UserCompanyPosition userCompanyPosition = new UserCompanyPosition(user, companyPosition);

    hibernateSession.saveOrUpdate(userCompanyPosition);
    userCompanyPosition.notifyClientAboutChange(hibernateSession);

    // These both lines are not necessary in the live system. 
    // But without them, the test below fails:
    user.getUserCompanyPositions().add(userCompanyPosition); 
    companyPosition.getUserCompanyPositions().add(userCompanyPosition);

    return userCompanyPosition;
}

The test:

public void testCreateObject() throws Exception {

    controller.createUserCompanyPosition(existingUser, existingCompanyPosition);

    assertEquals(companyPosition.getUserCompanyPositions().get(0),
        user.getUserCompanyPositions().get(0));
        // the queried collection user.getCompanyPositions() is empty, 
        // when we don't explicitly add the references
}

Maybe try setting this property in your application properties for test evnironment.

spring.jpa.hibernate.ddl-auto=create-drop

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM