HHH-11186 - Add examples for all Hibernate annotations

Document @UniqueConstraint annotation
This commit is contained in:
Vlad Mihalcea 2017-06-01 13:57:40 +03:00
parent 87ddf0e74b
commit c0b0da4282
7 changed files with 330 additions and 1 deletions

View File

@ -16,6 +16,8 @@ See the <<chapters/domain/access.adoc#access,Access type>> section for more info
The http://docs.oracle.com/javaee/7/api/javax/persistence/AssociationOverride.html[`@AssociationOverride`] annotation is used to override an association mapping (e.g. `@ManyToOne`, `@OneToOne`, `@OneToMany`, `@ManyToMany`) inherited from a mapped superclass or an embeddable.
//TODO: Add example
[[annotations-jpa-associationoverrides]]
==== `@AssociationOverrides`
@ -26,6 +28,8 @@ The http://docs.oracle.com/javaee/7/api/javax/persistence/AssociationOverrides.h
The http://docs.oracle.com/javaee/7/api/javax/persistence/AttributeOverride.html[`@AttributeOverride`] annotation is used to override an attribute mapping inherited from a mapped superclass or an embeddable.
//TODO: Add example
[[annotations-jpa-attributeoverrides]]
==== `@AttributeOverrides`
@ -75,6 +79,8 @@ See the <<chapters/query/native/Native.adoc#sql-composite-key-entity-association
The http://docs.oracle.com/javaee/7/api/javax/persistence/ConstructorResult.html[`@ConstructorResult`] annotation is used in conjunction with the <<annotations-jpa-sqlresultsetmapping>> annotations to map columns of a given SELECT query to a certain object constructor.
//TODO: Add example
[[annotations-jpa-convert]]
==== `@Convert`
@ -176,11 +182,15 @@ See the <<chapters/domain/basic_types.adoc#basic-enums-Enumerated, `@Enumerated`
The http://docs.oracle.com/javaee/7/api/javax/persistence/ExcludeDefaultListeners.html[`@ExcludeDefaultListeners`] annotation is used to specify that the current annotated entity skips the invocation of any default listener.
//TODO: Add example
[[annotations-jpa-excludesuperclasslisteners]]
==== `@ExcludeSuperclassListeners`
The http://docs.oracle.com/javaee/7/api/javax/persistence/ExcludeSuperclassListeners.html[`@ExcludeSuperclassListeners`] annotation is used to specify that the current annotated entity skips the invocation of listeners declared by its superclass.
//TODO: Add example
[[annotations-jpa-fieldresult]]
==== `@FieldResult`
@ -225,6 +235,8 @@ See the <<chapters/domain/identifiers.adoc#identifiers-composite-nonaggregated,C
The http://docs.oracle.com/javaee/7/api/javax/persistence/Index.html[`@Index`] annotation is used by the automated schema generation tool to create a database index.
//TODO: Add example
[[annotations-jpa-inheritance]]
==== `@Inheritance`
@ -244,6 +256,8 @@ See the <<chapters/domain/associations.adoc#associations-many-to-one-example,`@M
The http://docs.oracle.com/javaee/7/api/javax/persistence/JoinColumns.html[`@JoinColumns`] annotation is used to group multiple <<annotations-jpa-joincolumn>> annotations, which are used when mapping entity association or an embeddable collection using a composite identifier
//TODO: Add example
[[annotations-jpa-jointable]]
==== `@JoinTable`
@ -284,11 +298,15 @@ See the <<chapters/domain/collections.adoc#collections-map-unidirectional-exampl
The http://docs.oracle.com/javaee/7/api/javax/persistence/MapKeyClass.html[`@MapKeyClass`] annotation is used to specify the type of the map key of a `java.util.Map` associations.
//TODO: Add example
[[annotations-jpa-mapkeycolumn]]
==== `@MapKeyColumn`
The http://docs.oracle.com/javaee/7/api/javax/persistence/MapKeyColumn.html[`@MapKeyColumn`] annotation is used to specify the database column which stores the key of a `java.util.Map` association for which the map key is a basic type.
//TODO: Add example
[[annotations-jpa-mapkeyenumerated]]
==== `@MapKeyEnumerated`
@ -309,6 +327,8 @@ See the <<chapters/domain/collections.adoc#collections-map-value-type-entity-key
The http://docs.oracle.com/javaee/7/api/javax/persistence/MapKeyJoinColumns.html[`@MapKeyJoinColumns`] annotation is used to group several <<annotations-jpa-mapkeyjoincolumn>> mappings when the `java.util.Map` association key uses a composite identifier.
//TODO: Add example
[[annotations-jpa-mapkeytemporal]]
==== `@MapKeyTemporal`
@ -373,6 +393,8 @@ The http://docs.oracle.com/javaee/7/api/javax/persistence/NamedQueries.html[`@Na
The http://docs.oracle.com/javaee/7/api/javax/persistence/NamedQuery.html[`@NamedQuery`] annotation is used to specify a JPQL query that can be retrieved later by its name.
//TODO: Add example
[[annotations-jpa-namedstoredprocedurequeries]]
==== `@NamedStoredProcedureQueries`
@ -383,11 +405,15 @@ The http://docs.oracle.com/javaee/7/api/javax/persistence/NamedStoredProcedureQu
The http://docs.oracle.com/javaee/7/api/javax/persistence/NamedStoredProcedureQuery.html[`@NamedStoredProcedureQuery`] annotation is used to specify a stored procedure query that can be retrieved later by its name.
//TODO: Add example
[[annotations-jpa-namedsubgraph]]
==== `@NamedSubgraph`
The http://docs.oracle.com/javaee/7/api/javax/persistence/NamedSubgraph.html[`@NamedSubgraph`] annotation used to specify a subgraph in an Entity Graph.
//TODO: Add example
[[annotations-jpa-onetomany]]
==== `@OneToMany`
@ -421,6 +447,8 @@ See the <<chapters/domain/collections.adoc#collections-unidirectional-ordered-li
The http://docs.oracle.com/javaee/7/api/javax/persistence/PersistenceContext.html[`@PersistenceContext`] annotation is used to specify the `EntityManager` that needs to be injected as a dependency.
//TODO: Add example
[[annotations-jpa-persistencecontexts]]
==== `@PersistenceContexts`
@ -431,11 +459,15 @@ The http://docs.oracle.com/javaee/7/api/javax/persistence/PersistenceContexts.ht
The http://docs.oracle.com/javaee/7/api/javax/persistence/PersistenceProperty.html[`@PersistenceProperty`] annotation is used by the <<annotations-jpa-persistencecontext>> annotation to declare JPA provider properties that are passed to the underlying container when the `EntityManager` instance is created.
//TODO: Add example
[[annotations-jpa-persistenceunit]]
==== `@PersistenceUnit`
The http://docs.oracle.com/javaee/7/api/javax/persistence/PersistenceUnit.html[`@PersistenceUnit`] annotation is used to specify the `EntityManagerFactory` that needs to be injected as a dependency.
//TODO: Add example
[[annotations-jpa-persistenceunits]]
==== `@PersistenceUnits`
@ -508,6 +540,8 @@ The http://docs.oracle.com/javaee/7/api/javax/persistence/PrimaryKeyJoinColumns.
The http://docs.oracle.com/javaee/7/api/javax/persistence/QueryHint.html[`@QueryHint`] annotation is used to specify a JPA provider hint used by a `@NamedQuery` or a `@NamedNativeQuery` annotation.
//TODO: Add example
[[annotations-jpa-secondarytable]]
==== `@SecondaryTable`
@ -525,6 +559,8 @@ The http://docs.oracle.com/javaee/7/api/javax/persistence/SecondaryTables.html[`
The http://docs.oracle.com/javaee/7/api/javax/persistence/SequenceGenerator.html[`@SequenceGenerator`] annotation is used to specify the database sequence used by the identifier generator of the current annotated entity.
//TODO: Add example
[[annotations-jpa-sqlresultsetmapping]]
==== `@SqlResultSetMapping`
@ -542,6 +578,8 @@ The http://docs.oracle.com/javaee/7/api/javax/persistence/SqlResultSetMappings.h
The http://docs.oracle.com/javaee/7/api/javax/persistence/StoredProcedureParameter.html[`@StoredProcedureParameter`] annotation is used to specify a parameter of a <<annotations-jpa-namedstoredprocedurequery>>.
//TODO: Add example
[[annotations-jpa-table]]
==== `@Table`
@ -554,6 +592,8 @@ See the <<chapters/query/native/Native.adoc#sql-custom-crud-secondary-table-exam
The http://docs.oracle.com/javaee/7/api/javax/persistence/TableGenerator.html[`@TableGenerator`] annotation is used to specify the database table used by the identity generator of the current annotated entity.
//TODO: Add example
[[annotations-jpa-temporal]]
==== `@Temporal`
@ -573,6 +613,8 @@ See the <<chapters/events/Events.adoc#events-jpa-callbacks-example, `@Transient`
The http://docs.oracle.com/javaee/7/api/javax/persistence/UniqueConstraint.html[`@UniqueConstraint`] annotation is used to specify a unique constraint to be included by the automated schema generator for the primary or secondary table associated with the current annotated entity.
See the <<chapters/schema/Schema.adoc#schema-generation-columns-unique-constraint, Columns unique constraint>> chapter for more info.
[[annotations-jpa-version]]
==== `@Version`

View File

@ -159,3 +159,43 @@ include::{sourcedir}/ColumnDefaultTest.java[tag=schema-generation-column-default
include::{extrasdir}/schema-generation-column-default-value-persist-example.sql[]
----
====
[[schema-generation-columns-unique-constraint]]
=== Columns unique constraint
The http://docs.oracle.com/javaee/7/api/javax/persistence/UniqueConstraint.html[`@UniqueConstraint`] annotation is used to specify a unique constraint to be included by the automated schema generator for the primary or secondary table associated with the current annotated entity.
Considering the following entity mapping, Hibernate generates the unique constraint DDL when creating the database schema:
[[schema-generation-columns-unique-constraint-mapping-example]]
.`@UniqueConstraint` mapping example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/UniqueConstraintTest.java[tag=schema-generation-columns-unique-constraint-mapping-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/schema-generation-columns-unique-constraint-mapping-example.sql[]
----
====
With the `uk_book_title_author` unique constraint in place,
it's no longer possible to add two books with the same title and for the same author.
[[schema-generation-columns-unique-constraint-persist-example]]
.`@UniqueConstraintTest` persist example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/UniqueConstraintTest.java[tag=schema-generation-columns-unique-constraint-persist-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/schema-generation-columns-unique-constraint-persist-example.sql[]
----
====
The second INSERT statement fails because of the unique coonstraint violation.

View File

@ -0,0 +1,22 @@
create table author (
id bigint not null,
firstName varchar(255),
lastName varchar(255),
primary key (id)
)
create table book (
id bigint not null,
title varchar(255),
author_id bigint,
primary key (id)
)
alter table book
add constraint uk_book_title_author
unique (title, author_id)
alter table book
add constraint fk_book_author_id
foreign key (author_id)
references author

View File

@ -0,0 +1,35 @@
insert
into
author
(firstName, lastName, id)
values
(?, ?, ?)
-- binding parameter [1] as [VARCHAR] - [Vlad]
-- binding parameter [2] as [VARCHAR] - [Mihalcea]
-- binding parameter [3] as [BIGINT] - [1]
insert
into
book
(author_id, title, id)
values
(?, ?, ?)
-- binding parameter [1] as [BIGINT] - [1]
-- binding parameter [2] as [VARCHAR] - [High-Performance Java Persistence]
-- binding parameter [3] as [BIGINT] - [2]
insert
into
book
(author_id, title, id)
values
(?, ?, ?)
-- binding parameter [1] as [BIGINT] - [1]
-- binding parameter [2] as [VARCHAR] - [High-Performance Java Persistence]
-- binding parameter [3] as [BIGINT] - [3]
-- SQL Error: 23505, SQLState: 23505
-- Unique index or primary key violation: "UK_BOOK_TITLE_AUTHOR_INDEX_1 ON PUBLIC.BOOK(TITLE, AUTHOR_ID) VALUES ( /* key:1 */ 3, 'High-Performance Java Persistence', 1)";

View File

@ -0,0 +1,175 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* 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.userguide.schema;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.util.ExceptionUtil;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Vlad Mihalcea
*/
public class UniqueConstraintTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Book.class,
Author.class,
};
}
@Test
public void test() {
//tag::schema-generation-columns-unique-constraint-persist-example[]
Author _author = doInJPA( this::entityManagerFactory, entityManager -> {
Author author = new Author();
author.setFirstName( "Vlad" );
author.setLastName( "Mihalcea" );
entityManager.persist( author );
Book book = new Book();
book.setTitle( "High-Performance Java Persistence" );
book.setAuthor( author );
entityManager.persist( book );
return author;
} );
try {
doInJPA( this::entityManagerFactory, entityManager -> {
Book book = new Book();
book.setTitle( "High-Performance Java Persistence" );
book.setAuthor( _author );
entityManager.persist( book );
} );
}
catch (Exception expected) {
assertNotNull( ExceptionUtil.findCause( expected, ConstraintViolationException.class ) );
}
//end::schema-generation-columns-unique-constraint-persist-example[]
}
//tag::schema-generation-columns-unique-constraint-mapping-example[]
@Entity
@Table(
name = "book",
uniqueConstraints = @UniqueConstraint(
name = "uk_book_title_author",
columnNames = {
"title",
"author_id"
}
)
)
public static class Book {
@Id
@GeneratedValue
private Long id;
private String title;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
name = "author_id",
foreignKey = @ForeignKey(name = "fk_book_author_id")
)
private Author author;
//Getter and setters omitted for brevity
//end::schema-generation-columns-unique-constraint-mapping-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
//tag::schema-generation-columns-unique-constraint-mapping-example[]
}
@Entity
@Table(name = "author")
public static class Author {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String lastName;
//Getter and setters omitted for brevity
//end::schema-generation-columns-unique-constraint-mapping-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
//tag::schema-generation-columns-unique-constraint-mapping-example[]
}
//end::schema-generation-columns-unique-constraint-mapping-example[]
}

View File

@ -13,7 +13,6 @@ hibernate.connection.password @jdbc.pass@
hibernate.connection.pool_size 5
hibernate.show_sql true
hibernate.format_sql true
hibernate.max_fetch_depth 5

View File

@ -42,6 +42,22 @@ public class ExceptionUtil {
return t;
}
/**
* Get a specific cause.
*
* @param t exception
* @param causeClass cause type
*
* @return exception root cause
*/
public static Throwable findCause(Throwable t, Class<? extends Throwable> causeClass) {
Throwable cause = t.getCause();
if ( cause != null && !causeClass.equals( cause.getClass() ) ) {
return ( cause != t ) ? findCause( cause, causeClass ) : null;
}
return cause;
}
/**
* Was the given exception caused by a SQL lock timeout?
*