Skip to content

Commit ec16529

Browse files
committed
HHH-11186 - Add examples for all Hibernate annotations
Document @source and @ROWID annotation
1 parent e2e48e4 commit ec16529

File tree

10 files changed

+317
-3
lines changed

10 files changed

+317
-3
lines changed

documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,7 +1139,7 @@ For instance, Oracle defines the https://docs.oracle.com/cd/B19306_01/server.102
11391139

11401140
According to Oracle documentation, `ROWID` is the fastest way to access a single row from a table.
11411141

1142-
//TODO: Add example
1142+
See the <<chapters/domain/identifiers.adoc#identifiers-rowid, `@RowId` mapping>> section for more info.
11431143

11441144
[[annotations-hibernate-selectbeforeupdate]]
11451145
==== `@SelectBeforeUpdate`
@@ -1178,7 +1178,7 @@ The `SourceType` offers two options:
11781178
DB:: Get the timestamp from the database.
11791179
VM:: Get the timestamp from the current JVM.
11801180

1181-
//TODO: Add example
1181+
See the <<chapters/locking/Locking.adoc#locking-optimistic-version-timestamp-source-mapping-example, Database-generated version timestamp mapping>> section for more info.
11821182

11831183
[[annotations-hibernate-sqldelete]]
11841184
==== `@SQLDelete`
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
SELECT
2+
p.id as id1_0_0_,
3+
p."name" as name2_0_0_,
4+
p."number" as number3_0_0_,
5+
p.ROWID as rowid_0_
6+
FROM
7+
Product p
8+
WHERE
9+
p.id = ?
10+
11+
-- binding parameter [1] as [BIGINT] - [1]
12+
13+
-- extracted value ([name2_0_0_] : [VARCHAR]) - [Mobile phone]
14+
-- extracted value ([number3_0_0_] : [VARCHAR]) - [123-456-7890]
15+
-- extracted ROWID value: AAAwkBAAEAAACP3AAA
16+
17+
UPDATE
18+
Product
19+
SET
20+
"name" = ?,
21+
"number" = ?
22+
WHERE
23+
ROWID = ?
24+
25+
-- binding parameter [1] as [VARCHAR] - [Smart phone]
26+
-- binding parameter [2] as [VARCHAR] - [123-456-7890]
27+
-- binding parameter [3] as ROWID - [AAAwkBAAEAAACP3AAA]

documentation/src/main/asciidoc/userguide/chapters/domain/identifiers.adoc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,4 +469,35 @@ include::{extrasdir}/id/CompositeIdAssociationPrimaryKeyJoinColumn.java[]
469469
[NOTE]
470470
====
471471
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.
472+
====
473+
474+
[[identifiers-rowid]]
475+
==== @RowId
476+
477+
If you annotate a given entity with the `@RowId` annotation and the underlying database supports fetching a record by ROWID (e.g. Oracle),
478+
then Hibernate can use the `ROWID` pseudo-column for CRUD operations.
479+
480+
[[identifiers-rowid-mapping]]
481+
.`@RowId` entity maapping
482+
====
483+
[source, JAVA, indent=0]
484+
----
485+
include::{sourcedir}/RowIdTest.java[tag=identifiers-rowid-mapping]
486+
----
487+
====
488+
489+
Now, when fetching an entity and modifying it, Hibernate uses the `ROWID` pseudo-column for the UPDATE SQL statement.
490+
491+
[[identifiers-rowid-example]]
492+
.`@RowId` example
493+
====
494+
[source, JAVA, indent=0]
495+
----
496+
include::{sourcedir}/RowIdTest.java[tag=identifiers-rowid-example]
497+
----
498+
499+
[source, SQL, indent=0]
500+
----
501+
include::{extrasdir}/id/identifiers-rowid-example.sql[]
502+
----
472503
====

documentation/src/main/asciidoc/userguide/chapters/locking/Locking.adoc

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,33 @@ Hibernate can retrieve the timestamp value from the database or the JVM, by read
8282
The value can be either `org.hibernate.annotations.SourceType.DB` or `org.hibernate.annotations.SourceType.VM`.
8383
The default behavior is to use the database, and is also used if you don't specify the annotation at all.
8484

85-
The timestamp can also be generated by the database instead of Hibernate, if you use the `@org.hibernate.annotations.Generated(GenerationTime.ALWAYS)` annotation.
85+
The timestamp can also be generated by the database instead of Hibernate
86+
if you use the `@org.hibernate.annotations.Generated(GenerationTime.ALWAYS)` or the `@Source` annotation.
87+
88+
[[locking-optimistic-version-timestamp-source-mapping-example]]
89+
.Database-generated version timestamp mapping
90+
====
91+
[source, JAVA, indent=0]
92+
----
93+
include::{sourcedir}/VersionSourceTest.java[tags=locking-optimistic-version-timestamp-source-mapping-example]
94+
----
95+
====
96+
97+
Now, when persisting a `Person` entity, Hibernate calls the database-specific current timestamp retrieval function:
98+
99+
[[locking-optimistic-version-timestamp-source-persist-example]]
100+
.Database-generated version timestamp example
101+
====
102+
[source, JAVA, indent=0]
103+
----
104+
include::{sourcedir}/VersionSourceTest.java[tags=locking-optimistic-version-timestamp-source-persist-example]
105+
----
106+
107+
[source, SQL, indent=0]
108+
----
109+
include::{extrasdir}/locking-optimistic-version-timestamp-source-persist-example.sql[]
110+
----
111+
====
86112

87113
[[locking-pessimistic]]
88114
=== Pessimistic
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CALL current_timestamp()
2+
3+
INSERT INTO
4+
Person
5+
(firstName, lastName, version, id)
6+
VALUES
7+
(?, ?, ?, ?)
8+
9+
-- binding parameter [1] as [VARCHAR] - [John]
10+
-- binding parameter [2] as [VARCHAR] - [Doe]
11+
-- binding parameter [3] as [TIMESTAMP] - [2017-05-18 12:03:03.808]
12+
-- binding parameter [4] as [BIGINT] - [1]
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.userguide.locking;
8+
9+
import java.util.Date;
10+
import javax.persistence.Entity;
11+
import javax.persistence.Id;
12+
import javax.persistence.Version;
13+
14+
import org.hibernate.annotations.Source;
15+
import org.hibernate.annotations.SourceType;
16+
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
17+
18+
import org.junit.Test;
19+
20+
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
21+
import static org.junit.Assert.assertNotNull;
22+
import static org.junit.Assert.assertNull;
23+
24+
/**
25+
* @author Vlad Mihalcea
26+
*/
27+
public class VersionSourceTest extends BaseEntityManagerFunctionalTestCase {
28+
29+
@Override
30+
protected Class<?>[] getAnnotatedClasses() {
31+
return new Class<?>[] {
32+
Person.class
33+
};
34+
}
35+
36+
@Test
37+
public void test() {
38+
doInJPA( this::entityManagerFactory, entityManager -> {
39+
//tag::locking-optimistic-version-timestamp-source-persist-example[]
40+
Person person = new Person();
41+
person.setId( 1L );
42+
person.setFirstName( "John" );
43+
person.setLastName( "Doe" );
44+
assertNull( person.getVersion() );
45+
46+
entityManager.persist( person );
47+
assertNotNull( person.getVersion() );
48+
//end::locking-optimistic-version-timestamp-source-persist-example[]
49+
} );
50+
}
51+
52+
//tag::locking-optimistic-version-timestamp-source-mapping-example[]
53+
@Entity(name = "Person")
54+
public static class Person {
55+
56+
@Id
57+
private Long id;
58+
59+
private String firstName;
60+
61+
private String lastName;
62+
63+
@Version
64+
@Source(value = SourceType.DB)
65+
private Date version;
66+
//end::locking-optimistic-version-timestamp-source-mapping-example[]
67+
68+
public Long getId() {
69+
return id;
70+
}
71+
72+
public void setId(Long id) {
73+
this.id = id;
74+
}
75+
76+
public String getFirstName() {
77+
return firstName;
78+
}
79+
80+
public void setFirstName(String firstName) {
81+
this.firstName = firstName;
82+
}
83+
84+
public String getLastName() {
85+
return lastName;
86+
}
87+
88+
public void setLastName(String lastName) {
89+
this.lastName = lastName;
90+
}
91+
92+
public Date getVersion() {
93+
return version;
94+
}
95+
96+
//tag::locking-optimistic-version-timestamp-source-mapping-example[]
97+
}
98+
//end::locking-optimistic-version-timestamp-source-mapping-example[]
99+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.userguide.mapping.identifier;
8+
9+
import javax.persistence.Column;
10+
import javax.persistence.Entity;
11+
import javax.persistence.Id;
12+
13+
import org.hibernate.annotations.Formula;
14+
import org.hibernate.annotations.RowId;
15+
import org.hibernate.dialect.Oracle8iDialect;
16+
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
17+
18+
import org.hibernate.testing.RequiresDialect;
19+
import org.junit.Test;
20+
21+
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
22+
23+
/**
24+
* @author Vlad Mihalcea
25+
*/
26+
@RequiresDialect(Oracle8iDialect.class)
27+
public class RowIdTest extends BaseEntityManagerFunctionalTestCase {
28+
29+
@Override
30+
protected Class<?>[] getAnnotatedClasses() {
31+
return new Class<?>[] {
32+
Product.class
33+
};
34+
}
35+
36+
@Test
37+
public void test() {
38+
doInJPA( this::entityManagerFactory, entityManager -> {
39+
Product product = new Product();
40+
product.setId( 1L );
41+
product.setName( "Mobile phone" );
42+
product.setNumber( "123-456-7890" );
43+
entityManager.persist( product );
44+
} );
45+
doInJPA( this::entityManagerFactory, entityManager -> {
46+
//tag::identifiers-rowid-example[]
47+
Product product = entityManager.find( Product.class, 1L );
48+
49+
product.setName( "Smart phone" );
50+
//end::identifiers-rowid-example[]
51+
} );
52+
}
53+
54+
//tag::identifiers-rowid-mapping[]
55+
@Entity(name = "Product")
56+
@RowId("ROWID")
57+
public static class Product {
58+
59+
@Id
60+
private Long id;
61+
62+
@Column(name = "`name`")
63+
private String name;
64+
65+
@Column(name = "`number`")
66+
private String number;
67+
68+
//Getters and setters are omitted for brevity
69+
70+
//end::identifiers-rowid-mapping[]
71+
72+
public Long getId() {
73+
return id;
74+
}
75+
76+
public void setId(Long id) {
77+
this.id = id;
78+
}
79+
80+
public String getName() {
81+
return name;
82+
}
83+
84+
public void setName(String name) {
85+
this.name = name;
86+
}
87+
88+
public String getNumber() {
89+
return number;
90+
}
91+
92+
public void setNumber(String number) {
93+
this.number = number;
94+
}
95+
96+
97+
//tag::identifiers-rowid-mapping[]
98+
}
99+
//end::identifiers-rowid-mapping[]
100+
}

documentation/src/test/resources/log4j.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ log4j.logger.org.hibernate.SQL=debug
2727
### log JDBC bind parameters ###
2828
log4j.logger.org.hibernate.type=trace
2929
log4j.logger.org.hibernate.type.descriptor.sql=trace
30+
log4j.logger.org.hibernate.persister.entity.AbstractEntityPersister=trace
31+
log4j.logger.org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl=trace
3032

3133
### log schema export/update ###
3234
log4j.logger.org.hibernate.tool.hbm2ddl=info

hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/EntityReferenceInitializerImpl.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,13 @@ private void loadFromResultSet(
328328
rowId = concreteEntityPersister.hasRowId()
329329
? resultSet.getObject( entityReferenceAliases.getColumnAliases().getRowIdAlias() )
330330
: null;
331+
332+
if ( rowId != null && log.isTraceEnabled() ) {
333+
log.tracev(
334+
"extracted ROWID value: {0}",
335+
rowId
336+
);
337+
}
331338
}
332339
catch (SQLException e) {
333340
throw context.getSession().getFactory().getServiceRegistry().getService( JdbcServices.class ).getSqlExceptionHelper().convert(

hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2746,6 +2746,16 @@ private int dehydrateId(
27462746
final SharedSessionContractImplementor session,
27472747
int index) throws SQLException {
27482748
if ( rowId != null ) {
2749+
if ( LOG.isTraceEnabled() ) {
2750+
LOG.tracev(
2751+
String.format(
2752+
"binding parameter [%s] as ROWID - [%s]",
2753+
index,
2754+
rowId
2755+
)
2756+
);
2757+
}
2758+
27492759
ps.setObject( index, rowId );
27502760
return 1;
27512761
}

0 commit comments

Comments
 (0)