HHH-10826 Auxiliary Database Object no longer documented

HHH-10334 @Formula annotation javadoc contains database dependent sample usage

Created a new section related to automatic schema generation
This commit is contained in:
Vlad Mihalcea 2016-06-20 12:34:26 +03:00
parent 60aac117e6
commit 69ee03a79c
9 changed files with 354 additions and 2 deletions

View File

@ -10,6 +10,7 @@ include::Preface.adoc[]
include::chapters/architecture/Architecture.adoc[]
include::chapters/domain/DomainModel.adoc[]
include::chapters/bootstrap/Bootstrap.adoc[]
include::chapters/schema/Schema.adoc[]
include::chapters/pc/PersistenceContext.adoc[]
include::chapters/flushing/Flushing.adoc[]
include::chapters/jdbc/Database_Access.adoc[]

View File

@ -556,7 +556,7 @@ Hibernate historically also accepted `hibernate.hbm2ddl.import_files` for a simi
Comma-separated names of the optional files containing SQL DML statements executed during the `SessionFactory` creation.
File order matters, the statements of a give file are executed before the statements of the following one.
These statements are only executed if the schema is created, meaning that `hibernate.hbm2ddl.auto` is set to `create` or `create-drop`.
These statements are only executed if the schema is created, meaning that `hibernate.hbm2ddl.auto` is set to `create`, `create-drop`, or `update`.
`javax.persistence.schema-generation.create-script-source` / `javax.persistence.schema-generation.drop-script-source` should be preferred.
|`javax.persistence.sql-load-script-source` | |

View File

@ -1172,6 +1172,11 @@ include::{extrasdir}/basic/mapping-column-read-and-write-composite-type-persiste
Sometimes, you want the Database to do some computation for you rather than in the JVM, you might also create some kind of virtual column.
You can use a SQL fragment (aka formula) instead of mapping a property into a column. This kind of property is read only (its value is calculated by your formula fragment)
[NOTE]
====
You should be aware that the `@Formula` annotation takes a native SQL clause which can affect database portability.
====
[[mapping-column-formula-example]]
.`@Formula` mapping usage
====

View File

@ -0,0 +1,87 @@
[[schema-generation]]
== Schema generation
:sourcedir: ../../../../../test/java/org/hibernate/userguide/schema
:extrasdir: extras
:resourcesdir: ../../../../../test/resources
Hibernate allows you to generate the database from the entity mappings.
[TIP]
====
Although the automatic schema generation is very useful for testing and prototyping purposes, in a production environment,
it's much more flexible to manage the schema using incremental migration scripts.
====
Traditionally, the process of generating schema from entity mapping has been called `HBM2DDL`.
To get a list of Hibernate-native and JPA-specific configuration properties consider reading the <<appendices/Configurations.adoc#configurations-hbmddl,Configurations>> section.
Considering the following Domain Model:
[[schema-generation-domain-model-example]]
.Schema generation Domain Model
====
[source, JAVA, indent=0]
----
include::{sourcedir}/SchemaGenerationTest.java[tags=schema-generation-domain-model-example]
----
====
If the `hibernate.hbm2ddl.auto` configuration is set to `create`, Hibernate is going to generate the following database schema:
[[sql-schema-generation-domain-model-example]]
.Auto-generated database schema
====
[source, SQL, indent=0]
----
include::{extrasdir}/sql-schema-generation-domain-model-example.sql[]
----
====
=== Importing script files
To customize the schema generation process, the `hibernate.hbm2ddl.import_files` configuration property must be used to provide other scripts files that Hibernate can use when the `SessionFactory` is started.
For instance, considering the following `schema-generation.sql` import file:
[[schema-generation-import-file-example]]
.Schema generation import file
====
[source, JAVA, indent=0]
----
include::{resourcesdir}/schema-generation.sql[]
----
====
If we configure Hibernate to import the script above:
[[schema-generation-import-file-configuration-example]]
.Enabling query cache
====
[source, XML, indent=0]
----
<property
name="hibernate.hbm2ddl.import_files"
value="schema-generation.sql" />
----
====
Hibernate is going to execute the script file after the schema is automatically generated.
=== Database objects
Hibernate allows you to customize the schema generation process via the HBM `database-object` element.
Considering the following HBM mapping:
[[schema-generation-database-object-example]]
.Schema generation HBM database-object
====
[source, JAVA, indent=0]
----
include::{sourcedir}/SchemaGenerationTest.hbm.xml[]
----
====
When the `SessionFactory` is bootstrapped, Hibernate is going to execute the `database-object`, therefore creating the `sp_count_books` funtion.

View File

@ -0,0 +1,29 @@
create table Customer (
id integer not null,
accountsPayableXrefId binary,
image blob,
name varchar(255),
primary key (id)
)
create table Book (
id bigint not null,
isbn varchar(255),
title varchar(255),
author_id bigint,
primary key (id)
)
create table Person (
id bigint not null,
name varchar(255),
primary key (id)
)
alter table Book
add constraint UK_u31e1frmjp9mxf8k8tmp990i unique (isbn)
alter table Book
add constraint FKrxrgiajod1le3gii8whx2doie
foreign key (author_id)
references Person

View File

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<database-object>
<create>
CREATE OR REPLACE FUNCTION sp_count_books(
IN authorId bigint,
OUT bookCount bigint)
RETURNS bigint AS
$BODY$
BEGIN
SELECT COUNT(*) INTO bookCount
FROM book
WHERE author_id = authorId;
END;
$BODY$
LANGUAGE plpgsql;
</create>
<drop></drop>
<dialect-scope name="org.hibernate.dialect.PostgreSQL95Dialect" />
</database-object>
</hibernate-mapping>

View File

@ -0,0 +1,204 @@
/*
* 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 java.sql.Blob;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.annotations.LazyGroup;
import org.hibernate.annotations.NaturalId;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.PostgreSQL81Dialect;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.RequiresDialect;
import org.junit.Test;
/**
* @author Vlad Mihalcea
*/
public class SchemaGenerationTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class,
Book.class,
Customer.class
};
}
@Override
protected void addConfigOptions(Map options) {
if ( getDialect().getClass().equals( H2Dialect.class ) ) {
options.put(
AvailableSettings.HBM2DDL_IMPORT_FILES,
"schema-generation.sql"
);
options.put( org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO, "update" );
}
}
@Override
protected String[] getMappings() {
if ( PostgreSQL81Dialect.class.isAssignableFrom( getDialect().getClass() ) ) {
return new String[] { "org/hibernate/userguide/schema/SchemaGenerationTest.hbm.xml" };
}
return super.getMappings();
}
@Test
@RequiresDialect( H2Dialect.class )
public void testH2() {
}
@Test
@RequiresDialect( PostgreSQL81Dialect.class )
public void testPostgres() {
}
//tag::schema-generation-domain-model-example[]
@Entity(name = "Customer")
public class Customer {
@Id
private Integer id;
private String name;
@Basic( fetch = FetchType.LAZY )
private UUID accountsPayableXrefId;
@Lob
@Basic( fetch = FetchType.LAZY )
@LazyGroup( "lobs" )
private Blob image;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public UUID getAccountsPayableXrefId() {
return accountsPayableXrefId;
}
public void setAccountsPayableXrefId(UUID accountsPayableXrefId) {
this.accountsPayableXrefId = accountsPayableXrefId;
}
public Blob getImage() {
return image;
}
public void setImage(Blob image) {
this.image = image;
}
}
@Entity(name = "Person")
public static class Person {
@Id
private Long id;
private String name;
@OneToMany(mappedBy = "author")
private List<Book> books = new ArrayList<>( );
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 List<Book> getBooks() {
return books;
}
}
@Entity(name = "Book")
public static class Book {
@Id
private Long id;
private String title;
@NaturalId
private String isbn;
@ManyToOne
private Person author;
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 Person getAuthor() {
return author;
}
public void setAuthor(Person author) {
this.author = author;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
}
//end::schema-generation-domain-model-example[]
}

View File

@ -0,0 +1 @@
create sequence book_sequence start with 1 increment by 1

View File

@ -27,7 +27,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* </pre></blockquote>
*
* <blockquote><pre>
* // call functions
* // call database functions ( e.g. MySQL upper() and substring() )
* &#064;Formula( "upper( substring( middle_name, 1 ) )" )
* Character getMiddleInitial() { ... }
* </pre></blockquote>