0

I have an entity with a primary key and a unique constraint as below

@Entity
@Table(name = "EMPLOYEE")
public class Employee {

@Id
@GeneratedValue
@Column(name = "ID", nullable = false)
private Integer id;

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

@Column(name = "USER_ID", unique = true, nullable = false)
private String userId;

@Column(name = "PASSWORD")
private String password;

public Employee() {
    this("", "", "");
}

public Employee(String name, String userId, String password) {
    this(null, name, userId, password);
}

public Employee(Integer id, String name, String userId, String password) {
    if (id != null)
        this.id = id;
    this.name = name;
    this.userId = userId;
    this.password = password;
}
}
  1. At various places in my code for unit tests I call my new Employee(...) and I dont persist these objects.
  2. After a few such instances, I am trying to do session.save(new Employee()) for the first time which gives me a ConstraintViolationException on USER_ID with an Id value greater than 1. And instead if i try persisting (session.save(new Employee("","userID","")) works fine.

Now I am sure that there are objects of Employee classes in the heap and which are transient(as known to my knowledge). The question is will session.save try to save all these objects.

If yes - what should I do avoid from saving all the instances.

If no - Can anybody tell me why am I getting a ConstraintViolationException.

PS: after the exception the no data appears in the database (not even a single entity) hence I am suspecting the save method might be doing something with transient objects and doesnt commit it. I have tried setting autocommit = true and no luck with that too.

Sampath
  • 103
  • 1
  • 8

2 Answers2

0

The session does not know about objects created with new Employee(...) if they are never passed to the session via save, merge, etc

This means the objects created before the first call to save are not related to the problem.

When calling session.save(new Employee()), Hibernate will create an id for the object for example via a sequence, and then persist it only when the session is flushed and committed.

The problem seems to be that there is already some data in the database.

Angular University
  • 42,341
  • 15
  • 74
  • 81
  • Hi, Thanks for your time. But the exception ConstraintViolation isnt happening on Id. its occurring on USER_ID column. And its a clean slate db everytime created from the ddls. – Sampath Feb 02 '14 at 17:02
  • In that case it could be that the database is not being resetted between tests in the expected way. Can you post an example of a unit test class, and the configuration /way that the database is being reinitialized? If it's an in-memory database like hsqldb, it stays in memory for the duration of the process (between tests), and needs to be wipped before each test. – Angular University Feb 02 '14 at 17:11
  • ITs an h2db created fresh in setupclass method of junit. Interestingly if I run the testcase under concern alone it passes (cos I dont create new Employee() in this test class). And it fails while running with other test classes (note that the db is entirely different with diff path and fresh creation from ddl). This made me to think about the way hibernate handling transients. Am i missing something. I tried to isolate the test under concern and reproduce but couldn't. – Sampath Feb 02 '14 at 17:48
  • Actually I think the cause is other, because hibernate only persists the transients that we explicitly pass it. The reason I wanted to have a look at one of the unit tests is to see if you are using spring-test. This is because the spring context is by default kept around between tests http://docs.spring.io/spring/docs/2.5.5/reference/testing.html, if @DirtiesContext is not used. And the in-memory DB can stay around between tests, depending how we clean it – Angular University Feb 02 '14 at 17:55
  • Nope. I am not using Spring context and is there a way I can send the project zipped across? – Sampath Feb 02 '14 at 17:57
  • Yes sure, if you put it on github or in a zip in megafileupload i'll have a look at it. – Angular University Feb 02 '14 at 18:01
  • It does not compile. Which test fails? The schema gets dropped between classes but not between tests. have a look at this answer, try with identity columns, maybe you are missing the hibernate sequence http://stackoverflow.com/questions/2011528/hibernate-auto-increment-id – Angular University Feb 02 '14 at 20:57
  • Yes. For every test class there is a new schema. TestEmployeeDAO.testSaveEmployee() is failing. I am pretty sure it is not to do with id, since the id is incremented and I am getting an exception on USER_ID column. Shall post the exception stack trace later. – Sampath Feb 03 '14 at 04:08
  • Here is a part of stack trace com.simplersystems.planner.dao.PlannerDAOException: org.hibernate.exception.ConstraintViolationException: could not execute statement at com.simplersystems.planner.dao.EmployeeDAO.saveEmployee(EmployeeDAO.java:37) Caused by: org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "UNQ_EMP_INDEX_7 ON PUBLIC.EMPLOYEE(USER_ID) VALUES ( /* 6 */ '' )"; SQL statement: insert into EMPLOYEE (ID, NAME, PASSWORD, USER_ID) values (null, ?, ?, ?) [23505-173] at org.h2.message.DbException.getJdbcSQLException(DbException.java:331) – Sampath Feb 03 '14 at 14:42
0

A ConstraintViolation could be caused by either the UNIQUE constraint being violated or the NOT NULL constraint. I think it is the latter. The issue is that you are setting userId to empty string and many databases (e.g. Oracle, MySQL) will convert and store empty string as NULL. And you've set the column as nullable=false. I don't know how h2 database treats empty string, but it appears you can set a database compatibility mode so it's possible you are using a mode that treats empty string as null.

Try either removing nullable=false (and the NOT NULL constraint in the database), or always setting userID to some non-null, non-empty value before persisting.

neildo
  • 2,206
  • 15
  • 12