Skip to content

Commit

Permalink
HHH-10826 Auxiliary Database Object no longer documented
Browse files Browse the repository at this point in the history
HHH-10334 @formula annotation javadoc contains database dependent sample usage

Created a new section related to automatic schema generation
  • Loading branch information
vladmihalcea committed Jun 20, 2016
1 parent 60aac11 commit 69ee03a
Show file tree
Hide file tree
Showing 9 changed files with 354 additions and 2 deletions.
Expand Up @@ -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[]
Expand Down
Expand Up @@ -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` | |
Expand Down
Expand Up @@ -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
====
Expand Down
@@ -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.

@@ -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
@@ -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>
@@ -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[]
}
1 change: 1 addition & 0 deletions documentation/src/test/resources/schema-generation.sql
@@ -0,0 +1 @@
create sequence book_sequence start with 1 increment by 1
Expand Up @@ -27,7 +27,7 @@
* </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>
Expand Down

0 comments on commit 69ee03a

Please sign in to comment.