Fencer
Fencer

Reputation: 1088

How do I get "no cascading"-behaviour using spring-boot, spring-data-jpa and hibernate?

I'm using the mentioned libraries and want to persist a simple object, that has a one-to-one-relationship with another, without persisting the associated object. That might sound simple, because it should be the default behaviour. However, in my case for some reason it seems not to be.

I'm using an entityManager configured by this code:

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, Environment env) {
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setDatabase(Database.POSTGRESQL);

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setDataSource(dataSource);

    return factory;
}

@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(entityManagerFactory);
    return transactionManager;
}

Here are my entity classes:

@Entity
@Table(name = "`Table_A`", schema = "public")
public class ClassA {
    @Id
    @Column(name = "a_id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "value")
    private String value;

    @Column(name = "time")
    private LocalDateTime time;

    @OneToOne(targetEntity = ClassB.class, fetch = FetchType.EAGER)
    @JoinColumn(nullable = false, name = "b_id_ref")
    private ClassB b;

    // getters and setters
}

@Entity
@Table(name = "`Table_B`", schema = "public")
public class ClassB {
    @Id
    @Column(name = "b_id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Column(name = "value")
    private String value;

    // getters and setters
}

I don't want the object that b in ClassA references to be persisted. But if I change value in an instance of ClassA and also value in the corresponding instance of ClassB. I wrote a small test to demonstrate the program flow:

@Autowired
private ClassARepository repository;

@PersistenceContext
private EntityManager em;

@Test
public void testCascading() {
    Session hibernateSession = em.unwrap(Session.class);
    String newValueA = "Sissi";
    String newValueB = "Franzl";
    ClassA a = createTestInstance();
    ClassB b = a.getB();
    String oldValueA = token.getTokenValue();
    String oldValueB = user.getFirstname();

    a.setValue(newValueA);
    b.setValue(newValueB);
    repository.save(a);
    em.flush();
    hibernateSession.evict(a);
    hibernateSession.evict(b);

    ClassA loadedA = repository.findById(1L);
    assertThat(loadedA.getB().getValue(), equalTo(oldValueB));
    assertThat(loadedA.getValue(), equalTo(newValueA));
}

I checked that the bahaviour is the same in the "real world". Like it is stated here the save-operation should not be cascaded. I must miss something here. Is there maybe a global setting that changes the default behaviour? What can I do to achieve the desired behaviour?

Upvotes: 2

Views: 564

Answers (1)

JB Nizet
JB Nizet

Reputation: 692071

Your test is transactional. So you're getting a managed B, and changing the value of one of its field. And the change is thus made persistent.

Your calls to save() and flush() are useless, by the way. The whole point of JPA is that when entities are managed, their state is made persistent automatically. It's a feature, not a bug.

Upvotes: 4

Related Questions