3

I have two objects with one-to-many relationship between them that I implemented as follows:

@Entity
@Table(name = "details")
public class MainDetails {
     @Id
     @Column(name = "details_id")
     @GeneratedValue(strategy= GenerationType.AUTO)
     private Long id;

     // Some other fields here - omitted

     @OneToMany(fetch = FetchType.LAZY,
                mappedBy = "details",
                targetEntity = State.class,
                cascade = CascadeType.ALL)
     @OrderBy("timestamp DESC")
     private List<State> states;
}

And the second one:

@Entity
@Table(name = "state")
public class State {

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

     @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "details_id")
     private MainDetails details;

     // Other fields omitted (including timestamp)

}

I call a save() method on the MainDetails object. The method looks like this:

public T save(T obj) { // The T in this case is MainDetails
  entityManager.persist(obj);
  entityManager.flush();
  return obj;
}

But then I get this exception:

Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "details_pkey" Detail: Key (details_id)=(8) already exists.

My persistence.xml looks like this:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
         version="2.0">
  <persistence-unit name="my-persistence-unit">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <!-- Annotated entity classes -->
    <class>com.company.entity.MainDetails</class>
    <class>com.company.entity.State</class>

    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL82Dialect" />
      <property name="hibernate.hbm2ddl.auto" value="create-drop" />
      <property name="hibernate.enable_lazy_load_no_trans" value="true"/>
    </properties>
  </persistence-unit>
</persistence>

My spring context looks like this:

@EnableJpaRepositories(basePackages = {"com.company.dao", "com.company.*.dao"})
@EnableTransactionManagement(proxyTargetClass = true)
@Import(BasicConfig.class)
public class DbConfig {

  @Value("${db.connection_string}")
  private String connectionString;

  @Value("${db.user_name}")
  private String dbUserName;

  @Value("${db.password}")
  private String dbPassword;

  @Bean
  public DataSource dataSource() {
    DriverManagerDataSource driver = new DriverManagerDataSource();
    driver.setDriverClassName("org.postgresql.Driver");
    driver.setUrl(connectionString);
    driver.setUsername(dbUserName);
    driver.setPassword(dbPassword);
    return driver;
  }

  @Bean
  public JpaVendorAdapter jpaVendorAdapter() {
    HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
    adapter.setShowSql(true);
    adapter.setGenerateDdl(true);
    adapter.setDatabase(Database.POSTGRESQL);
    return adapter;
  }

  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws ClassNotFoundException {
    LocalContainerEntityManagerFactoryBean factoryBean = new  LocalContainerEntityManagerFactoryBean();
    factoryBean.setDataSource(dataSource());
    factoryBean.setPersistenceUnitName("my-persistence-unit");
    factoryBean.setJpaVendorAdapter(jpaVendorAdapter());

    return factoryBean;
  }

  @Bean
  public JpaTransactionManager transactionManager() throws ClassNotFoundException {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());

    return transactionManager;
  }
}
5
  • Can you clean you details table and retry. It already has ID 8 in it. And is the obj in entityManager.persist(obj); already has id field populated. Is so call merge method. Commented Apr 4, 2016 at 7:52
  • @MadhusudanaReddySunnapu I can't clean the table :/ It's a table that is filled using python script which I now migrate to java. I don't want to delete the history. Isn't Hibernate supposed to figure out the table is already populated and assign an ID accordingly? Because it doesn't matter who populated the table - if I shut the Java process down and restart it, the table will be populated again anyway and Hibernate should figure this out. Doesn't it? Commented Apr 4, 2016 at 7:57
  • Hibernate can do that. But you need to use same PK generator in both hibernate and Python app. How is ID populated. Is it a sequence, identity column etc? Commented Apr 4, 2016 at 8:18
  • I think you figured it out. Commented Apr 4, 2016 at 8:24
  • @MadhusudanaReddySunnapu - Thanks! Just for the sake of those who would like to read further - I use (for both tables) a serial column type for the id. Commented Apr 4, 2016 at 8:26

1 Answer 1

11

Ok, that was my mistake (naturally) and I changed 3 things in order for it to work:

  1. Changed both classes to have: @GeneratedValue(strategy= GenerationType.TABLE) (Originally I used GenerationType.AUTO).

  2. I figured out that the method that calls save() also calls entityManager.flush(). After removing the redundant line it solved the issue. Beats me why - I would expect hibernate to just do nothing on a redundant flush() call, but apparently it was a must in order to solve the issue.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.