/*
* Hibernate OGM, Domain model persistence for NoSQL datastores
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.ogm.datastore.neo4j.test.mapping;
import static java.util.Collections.singletonMap;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.hibernate.HibernateException;
import org.hibernate.annotations.NaturalId;
import org.hibernate.ogm.datastore.neo4j.BaseNeo4jDialect;
import org.hibernate.ogm.exception.EntityAlreadyExistsException;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
/**
* Test that unique constraints are created on Neo4j for:
* <ul>
* <li>{@link Id}</li>
* <li>{@link NaturalId}</li>
* <li>{@link Column} when {@link Column#unique()} is set to {@code true}</li>
* <li>{@link Table#uniqueConstraints()} is set</li>
* </ul>
*
* @author Davide D'Alto
*/
public class UniqueConstraintTest extends Neo4jJpaTestCase {
@Rule
public ExpectedException thrown = ExpectedException.none();
private EntityWithConstraints entityWithConstraints;
@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = { "tableConstraint" }))
static class EntityWithConstraints {
@Id
private String id;
@Column(unique = true)
private long uniqueColumn;
@NaturalId // causes the creation of a unique constraint
private String naturalId;
private String tableConstraint;
private String nonUniqueProperty;
public String getId() {
return id;
}
public void setId(String login) {
this.id = login;
}
public long getUniqueColumn() {
return uniqueColumn;
}
public void setUniqueColumn(long uniqueColumn) {
this.uniqueColumn = uniqueColumn;
}
public String getNaturalId() {
return naturalId;
}
public void setNaturalId(String naturalId) {
this.naturalId = naturalId;
}
public String getTableConstraint() {
return tableConstraint;
}
public void setTableConstraint(String constraint) {
this.tableConstraint = constraint;
}
public String getNonUniqueProperty() {
return nonUniqueProperty;
}
public void setNonUniqueProperty(String nonUniqueProperty) {
this.nonUniqueProperty = nonUniqueProperty;
}
}
@Before
public void setup() throws Exception {
final EntityManager em = getFactory().createEntityManager();
em.getTransaction().begin();
entityWithConstraints = new EntityWithConstraints();
entityWithConstraints.setId( "johndoe" );
entityWithConstraints.setUniqueColumn( 12345678 );
entityWithConstraints.setNaturalId( "John Doe" );
entityWithConstraints.setTableConstraint( "unique" );
entityWithConstraints.setNonUniqueProperty( "no constraints here" );
em.persist( entityWithConstraints );
em.getTransaction().commit();
em.close();
}
@Test
public void shouldThrowExceptionForDuplicatedNaturalId() throws Throwable {
thrown.expect( EntityAlreadyExistsException.class );
thrown.expectMessage( "OGM000067: Trying to insert an already existing entity" );
try {
final EntityManager em = getFactory().createEntityManager();
em.getTransaction().begin();
EntityWithConstraints duplicated = new EntityWithConstraints();
duplicated.setId( "login2" );
duplicated.setNaturalId( entityWithConstraints.getNaturalId() );
em.persist( duplicated );
em.getTransaction().commit();
em.close();
}
catch (Exception e) {
throw extract( EntityAlreadyExistsException.class, e );
}
}
@Test
public void shouldThrowExceptionForDuplicatedUniqueColumn() throws Throwable {
thrown.expect( EntityAlreadyExistsException.class );
thrown.expectMessage( "OGM000067: Trying to insert an already existing entity" );
try {
final EntityManager em = getFactory().createEntityManager();
em.getTransaction().begin();
EntityWithConstraints duplicated = new EntityWithConstraints();
duplicated.setId( "login2" );
duplicated.setUniqueColumn( entityWithConstraints.getUniqueColumn() );
em.persist( duplicated );
em.getTransaction().commit();
em.close();
}
catch (Exception e) {
throw extract( EntityAlreadyExistsException.class, e );
}
}
@Test
public void shouldThrowExceptionForDuplicatedTableUniqueConstraintColumn() throws Throwable {
thrown.expect( EntityAlreadyExistsException.class );
thrown.expectMessage( "OGM000067: Trying to insert an already existing entity" );
try {
final EntityManager em = getFactory().createEntityManager();
em.getTransaction().begin();
EntityWithConstraints duplicated = new EntityWithConstraints();
duplicated.setId( "login3" );
duplicated.setTableConstraint( entityWithConstraints.getTableConstraint() );
em.persist( duplicated );
em.getTransaction().commit();
em.close();
}
catch (Exception e) {
throw extract( EntityAlreadyExistsException.class, e );
}
}
/*
* Testing it with a native query, otherwise hibernate will notice that the id already exists in the db
*/
@Test
public void shouldThrowExceptionForDuplicatedIdentifierWithNativeQuery() throws Throwable {
thrown.expect( HibernateException.class );
thrown.expectMessage( "OGM001416: " + BaseNeo4jDialect.CONSTRAINT_VIOLATION_CODE );
executeCypherQuery( "CREATE (n:`UniqueConstraintTest$EntityWithConstraints` {id: {id}})", singletonMap( "id", (Object) entityWithConstraints.id ) );
}
@Test
// Not expecting any exception
public void shouldNotCreateConstraintForNonUniqueProperty() throws Exception {
final EntityManager em = getFactory().createEntityManager();
em.getTransaction().begin();
EntityWithConstraints duplicated = new EntityWithConstraints();
duplicated.setId( "login4" );
duplicated.setNonUniqueProperty( entityWithConstraints.getNonUniqueProperty() );
em.persist( duplicated );
em.getTransaction().commit();
em.close();
}
private <T extends Throwable> T extract(Class<T> class1, Exception e) throws Throwable {
Throwable cause = e;
while ( cause != null ) {
if ( cause.getClass().equals( class1 ) ) {
break;
}
cause = cause.getCause();
}
if ( cause == null ) {
throw e;
}
throw cause;
}
@Override
public Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { EntityWithConstraints.class };
}
}