HHH-11186 - Add examples for all Hibernate annotations

Document @Source and @RowId annotation
This commit is contained in:
Vlad Mihalcea 2017-05-18 12:14:15 +03:00
parent e2e48e4e07
commit ec165296c6
10 changed files with 317 additions and 3 deletions

View File

@ -1139,7 +1139,7 @@ For instance, Oracle defines the https://docs.oracle.com/cd/B19306_01/server.102
According to Oracle documentation, `ROWID` is the fastest way to access a single row from a table.
//TODO: Add example
See the <<chapters/domain/identifiers.adoc#identifiers-rowid, `@RowId` mapping>> section for more info.
[[annotations-hibernate-selectbeforeupdate]]
==== `@SelectBeforeUpdate`
@ -1178,7 +1178,7 @@ The `SourceType` offers two options:
DB:: Get the timestamp from the database.
VM:: Get the timestamp from the current JVM.
//TODO: Add example
See the <<chapters/locking/Locking.adoc#locking-optimistic-version-timestamp-source-mapping-example, Database-generated version timestamp mapping>> section for more info.
[[annotations-hibernate-sqldelete]]
==== `@SQLDelete`

View File

@ -0,0 +1,27 @@
SELECT
p.id as id1_0_0_,
p."name" as name2_0_0_,
p."number" as number3_0_0_,
p.ROWID as rowid_0_
FROM
Product p
WHERE
p.id = ?
-- binding parameter [1] as [BIGINT] - [1]
-- extracted value ([name2_0_0_] : [VARCHAR]) - [Mobile phone]
-- extracted value ([number3_0_0_] : [VARCHAR]) - [123-456-7890]
-- extracted ROWID value: AAAwkBAAEAAACP3AAA
UPDATE
Product
SET
"name" = ?,
"number" = ?
WHERE
ROWID = ?
-- binding parameter [1] as [VARCHAR] - [Smart phone]
-- binding parameter [2] as [VARCHAR] - [123-456-7890]
-- binding parameter [3] as ROWID - [AAAwkBAAEAAACP3AAA]

View File

@ -469,4 +469,35 @@ include::{extrasdir}/id/CompositeIdAssociationPrimaryKeyJoinColumn.java[]
[NOTE]
====
Unlike `@MapsId`, the application developer is responsible for ensuring that the identifier and the many-to-one (or one-to-one) association are in sync.
====
[[identifiers-rowid]]
==== @RowId
If you annotate a given entity with the `@RowId` annotation and the underlying database supports fetching a record by ROWID (e.g. Oracle),
then Hibernate can use the `ROWID` pseudo-column for CRUD operations.
[[identifiers-rowid-mapping]]
.`@RowId` entity maapping
====
[source, JAVA, indent=0]
----
include::{sourcedir}/RowIdTest.java[tag=identifiers-rowid-mapping]
----
====
Now, when fetching an entity and modifying it, Hibernate uses the `ROWID` pseudo-column for the UPDATE SQL statement.
[[identifiers-rowid-example]]
.`@RowId` example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/RowIdTest.java[tag=identifiers-rowid-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/id/identifiers-rowid-example.sql[]
----
====

View File

@ -82,7 +82,33 @@ Hibernate can retrieve the timestamp value from the database or the JVM, by read
The value can be either `org.hibernate.annotations.SourceType.DB` or `org.hibernate.annotations.SourceType.VM`.
The default behavior is to use the database, and is also used if you don't specify the annotation at all.
The timestamp can also be generated by the database instead of Hibernate, if you use the `@org.hibernate.annotations.Generated(GenerationTime.ALWAYS)` annotation.
The timestamp can also be generated by the database instead of Hibernate
if you use the `@org.hibernate.annotations.Generated(GenerationTime.ALWAYS)` or the `@Source` annotation.
[[locking-optimistic-version-timestamp-source-mapping-example]]
.Database-generated version timestamp mapping
====
[source, JAVA, indent=0]
----
include::{sourcedir}/VersionSourceTest.java[tags=locking-optimistic-version-timestamp-source-mapping-example]
----
====
Now, when persisting a `Person` entity, Hibernate calls the database-specific current timestamp retrieval function:
[[locking-optimistic-version-timestamp-source-persist-example]]
.Database-generated version timestamp example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/VersionSourceTest.java[tags=locking-optimistic-version-timestamp-source-persist-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/locking-optimistic-version-timestamp-source-persist-example.sql[]
----
====
[[locking-pessimistic]]
=== Pessimistic

View File

@ -0,0 +1,12 @@
CALL current_timestamp()
INSERT INTO
Person
(firstName, lastName, version, id)
VALUES
(?, ?, ?, ?)
-- binding parameter [1] as [VARCHAR] - [John]
-- binding parameter [2] as [VARCHAR] - [Doe]
-- binding parameter [3] as [TIMESTAMP] - [2017-05-18 12:03:03.808]
-- binding parameter [4] as [BIGINT] - [1]

View File

@ -0,0 +1,99 @@
/*
* 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.locking;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
import org.hibernate.annotations.Source;
import org.hibernate.annotations.SourceType;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/**
* @author Vlad Mihalcea
*/
public class VersionSourceTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class
};
}
@Test
public void test() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::locking-optimistic-version-timestamp-source-persist-example[]
Person person = new Person();
person.setId( 1L );
person.setFirstName( "John" );
person.setLastName( "Doe" );
assertNull( person.getVersion() );
entityManager.persist( person );
assertNotNull( person.getVersion() );
//end::locking-optimistic-version-timestamp-source-persist-example[]
} );
}
//tag::locking-optimistic-version-timestamp-source-mapping-example[]
@Entity(name = "Person")
public static class Person {
@Id
private Long id;
private String firstName;
private String lastName;
@Version
@Source(value = SourceType.DB)
private Date version;
//end::locking-optimistic-version-timestamp-source-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;
}
public Date getVersion() {
return version;
}
//tag::locking-optimistic-version-timestamp-source-mapping-example[]
}
//end::locking-optimistic-version-timestamp-source-mapping-example[]
}

View File

@ -0,0 +1,100 @@
/*
* 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.mapping.identifier;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.RowId;
import org.hibernate.dialect.Oracle8iDialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.RequiresDialect;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
@RequiresDialect(Oracle8iDialect.class)
public class RowIdTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Product.class
};
}
@Test
public void test() {
doInJPA( this::entityManagerFactory, entityManager -> {
Product product = new Product();
product.setId( 1L );
product.setName( "Mobile phone" );
product.setNumber( "123-456-7890" );
entityManager.persist( product );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::identifiers-rowid-example[]
Product product = entityManager.find( Product.class, 1L );
product.setName( "Smart phone" );
//end::identifiers-rowid-example[]
} );
}
//tag::identifiers-rowid-mapping[]
@Entity(name = "Product")
@RowId("ROWID")
public static class Product {
@Id
private Long id;
@Column(name = "`name`")
private String name;
@Column(name = "`number`")
private String number;
//Getters and setters are omitted for brevity
//end::identifiers-rowid-mapping[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
//tag::identifiers-rowid-mapping[]
}
//end::identifiers-rowid-mapping[]
}

View File

@ -27,6 +27,8 @@ log4j.logger.org.hibernate.SQL=debug
### log JDBC bind parameters ###
log4j.logger.org.hibernate.type=trace
log4j.logger.org.hibernate.type.descriptor.sql=trace
log4j.logger.org.hibernate.persister.entity.AbstractEntityPersister=trace
log4j.logger.org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl=trace
### log schema export/update ###
log4j.logger.org.hibernate.tool.hbm2ddl=info

View File

@ -328,6 +328,13 @@ public class EntityReferenceInitializerImpl implements EntityReferenceInitialize
rowId = concreteEntityPersister.hasRowId()
? resultSet.getObject( entityReferenceAliases.getColumnAliases().getRowIdAlias() )
: null;
if ( rowId != null && log.isTraceEnabled() ) {
log.tracev(
"extracted ROWID value: {0}",
rowId
);
}
}
catch (SQLException e) {
throw context.getSession().getFactory().getServiceRegistry().getService( JdbcServices.class ).getSqlExceptionHelper().convert(

View File

@ -2746,6 +2746,16 @@ public abstract class AbstractEntityPersister
final SharedSessionContractImplementor session,
int index) throws SQLException {
if ( rowId != null ) {
if ( LOG.isTraceEnabled() ) {
LOG.tracev(
String.format(
"binding parameter [%s] as ROWID - [%s]",
index,
rowId
)
);
}
ps.setObject( index, rowId );
return 1;
}