36

Even though I set the attribute to be @Column(unique=true), I still insert a duplicate entry.

@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(unique=true )
    private String name;

    ...
}

I set the name using regular EL in JSF. I did not create table using JPA

Kukeltje
  • 12,223
  • 4
  • 24
  • 47
Thang Pham
  • 38,125
  • 75
  • 201
  • 285
  • 3
    It would be neat if your JPA vendor (ie hibernate) took care of updating the schema in this scenario, but unfortunately it doesn't. I wonder if there is a bug/feature request for it – corydoras Aug 18 '10 at 01:10
  • 1
    hibernate annotation: @Column(unique=true) String userName; Below is my hibernate denerated DDL : CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `password` varchar(255) NOT NULL, `userName` varchar(255) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `userName` (`userName`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin still I am able to insert duplicate – udit khare Mar 03 '16 at 12:37

10 Answers10

30

The unique=true element of the Column annotation and / or the UniqueConstraint annotation that can be used at the table level are used to specify that a unique constraint is to be included in the generated DDL.

In other words, they don't do anything during the runtime, the verification is left to the database (which makes sense as unicity can't be tested at the Java level reliably1) and if for whatever reason you don't have the corresponding constraint(s) defined at the database level, nothing will happen.

Add the constraint manually:

ALTER TABLE Customer ADD CONSTRAINT customer_name_unq UNIQUE (name);

See also

1 Unless you acquire a table lock (ouch!), you can't check for unicity with a SQL query in a concurrent environment.

Pascal Thivent
  • 562,542
  • 136
  • 1,062
  • 1,124
  • You say that "Unless you acquire a table lock (ouch!), you can't check for unicity with a SQL query in a concurrent environment." But even a pragmatic check (if there already is a row with this attribute value) without a lock is helpful. It would be correct in, say, 99 % of all cases. In any case, a (quick and dirty) uniqueness check from the Java bean class is very much needed. – Gerd Wagner Jun 23 '14 at 14:25
23

For future users stumbling on this issue. There are lots of great suggestions here; read through them as well as your error messages; they will be enough to resolve your problem.

A few things I picked up in my quest to get @Column(unique=true) working. In my case I had several issues (I was using Spring Boot, FYI). To name a couple:

  • My application.properties was using spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect despite using MySQL 8. I fixed this with spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect. Check your version (I did this through the command line: mysql> STATUS).
  • I had a User class annotated as an @entity which meant that JPA was trying to create a user table which is one of MySQL's (as well as postgres) reserved keywords. I fixed this with @Table(name = "users").
adnauseam
  • 627
  • 6
  • 14
  • 2
    Big thumbs up for this comment. Stumbled upon this very issue as wel. – LaurensVijnck Mar 17 '19 at 22:54
  • 2
    for those scratching head on mysql 8 for this issue, using this answer solved it. spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect – Aadam Jun 20 '19 at 18:48
  • 2
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect generate with InnoDB engine – softwarevamp Jul 22 '19 at 08:23
12

I did not create table using JPA

Then you should add the unique constraint to your table in your CREATE statement, for example, if you are using MySQL:

create Customer (id int primary key, name varchar(255) unique);
Behrang
  • 46,888
  • 25
  • 118
  • 160
  • It worked for me Thanks! I also turned off auto table creation from JPA and `@Column(unique=true )` didn't work. – mazend May 24 '21 at 07:10
10

For InnoDB tables , there are limit for indexed columns. That means, you have to set max length for te field:

@Column(unique = true, length = 32)
private String name;
DCjanus
  • 394
  • 5
  • 9
9

Try specifying the unique constraint at the class level using

@Table(uniqueConstraints={@UniqueConstraint(columnNames={"name"})})
public class Customer {
...
    private String name;
}

@Column(unique=true) doesn't work for me but when I used @UniqueConstraint at the class level, the column in the database (MySql) was appropriately set as unique.

I hope this helps someone else who may face this same issue in the future

ElikemT
  • 91
  • 1
  • 2
  • I thought as much @Kukeltje but it doesn't work for me. I use springboot with hibernate as JPA provider and MySql dbms. However, as I pointed out, '@UniqueContraint' within '@Table' on the class level works perfectly for me. – ElikemT Jul 17 '17 at 10:13
  • "Doesn't work" is a little vague... Does it not generate the unique constraints on the fields? Or are they not enforced? Or... And what hibernate version do you use? And what Database? Please be as specific as possible, – Kukeltje Jul 17 '17 at 10:15
  • 3
    Thanks for your comments @Kukeltje. I have conducted tests on the two cases and found out that `@Column(unique=true)` actually works(unique constraint enforced) but only if the table does not already exist in the database. Using `@Column(unique=true)` afterwards (i.e when the table had been generated previously) does not update the table with the unique constraint. However `@UniqueConstraint` works for me all the time whether the table already exists or not. Hibernate version is 5.0.12, My database is MySQL – ElikemT Jul 17 '17 at 12:00
  • @Kukeltje , it did not have created the unique index for me as well. This is my annotation `@Column(columnDefinition = "varchar(100) not null", unique = true)`. But, if I remove the `columnDefinition` part, it works perfectly fine. – Koushik Roy Jan 31 '18 at 12:36
  • Update to the above comment : To solve the issue, I have moved the unique inside column definition and it worked. Looks like the column definition is dominating the unique property. – Koushik Roy Jan 31 '18 at 12:43
  • @ElikemT I tried your comments on spring boot 1.4.1. unique is only honoured when it is already defined in database. Table is not getting updated by providing unique=true... @Column(name="email", nullable=false, unique=true, length=30) this dont make any effect until I changed email colum property manually in db. – riteshmaurya Jun 15 '18 at 14:21
  • @ElikemT I tried your comments on spring boot 1.4.1. unique is only honoured when it is already defined in database. Table is not getting updated by providing unique=true... @Column(name="email", nullable=false, unique=true, length=30) this dont make any effect until I changed email colum property manually in db. – riteshmaurya Jun 15 '18 at 14:21
6

I was also facing the similar issue but got it resolved.

  • First, drop the table from Database

      drop table Customer;
    
  • Stop your spring boot application and launch it again.

    This worked for me.

Jim Simson
  • 2,774
  • 3
  • 22
  • 30
Saif Haider
  • 511
  • 1
  • 5
  • 16
1

If table already existing on database and table existing non unique records, jpa dont set unique constraint to column. For solution:

  1. clean duplicated records or delete all records.
  2. then try to set unique constraint.
eda
  • 11
  • 2
0

Make sure to delete the tables created by hibernate in your database. And then re-run your hibernate application again.

  • (This post does not seem to provide a [quality answer](https://stackoverflow.com/help/how-to-answer) to the question. Please either edit your answer and improve it, or just post it as a comment to the question.) – sɐunıɔןɐqɐp Sep 26 '18 at 07:42
0

MySQL Hibernate Object Relational Mapping - Alter Contains after schema was generated.

  1. In case you change your entity class [Customer] after the Hibernate generated the schemas, you can drop the schema and re-generate it. The constrains will apply.

  2. Instead of dropping schemas you can manually alter the table

ALTER TABLE Customer ADD CONSTRAINT customer_name_unq UNIQUE (name);

0

It also happens when you have added the validation in the middle of the running application where all the databases and tables were already created.

As @Column is the persistence level annotation, it only works when the unique constraint is found in the required fields.

So just add all required annotations, delete all tables and then re-run the application. In this way the application will again create all the tables marked with @Entity annotation and also add 'unique' constraint with it.

It is important to note that all @Column annotations will have the same behaviour hence it is advised to add all required persistence level validations before head so that you don't need to delete tables again and again.