Skip to content

Commit d18b2cb

Browse files
committed
Hibernate module + redo service registry with Providers + update DI modules
1 parent 7ff089b commit d18b2cb

File tree

29 files changed

+868
-67
lines changed

29 files changed

+868
-67
lines changed

docs/asciidoc/modules.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Available modules are listed next.
2121

2222
=== Data
2323
* link:modules/hikari[HikariCP]: A high-performance JDBC connection pool.
24+
* link:modules/hibernate[Hibernate]: Hibernate ORM module.
2425

2526
=== JSON
2627
* link:modules/jackson[Jackson]: Jackson module for Jooby.
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
== Hibernate
2+
3+
https://hibernate.org/orm/[Hibernate ORM] module.
4+
5+
=== Usage
6+
7+
1) Add the dependencies (hikari + hibernate):
8+
9+
[dependency, artifactId="jooby-hikari:DataSource via HikariCP, jooby-hibernate:Hibernate Module"]
10+
.
11+
12+
2) Add database driver (mySQL here):
13+
14+
[dependency, artifactId="mysql-connector-java"]
15+
.
16+
17+
3) Set database properties
18+
19+
.application.conf
20+
[source, properties]
21+
----
22+
db.url = "jdbc:mysql://localhost/mydb"
23+
db.user = myuser
24+
db.password = mypass
25+
----
26+
27+
28+
4) Install and use Hibernate
29+
30+
.Java
31+
[source, java, role="primary"]
32+
----
33+
import io.jooby.hikari.Hikari;
34+
35+
{
36+
install(new Hikari()); <1>
37+
38+
install(new Hibernate()); <2>
39+
40+
get("/", ctx -> {
41+
EntityManager em = require(EntityManager.class); <3>
42+
Transaction trx = em.getTransaction(); <4>
43+
try {
44+
trx.begin(); <5>
45+
46+
// work with EntityManager compute a result <6>
47+
48+
trx.commit(); <7>
49+
50+
return result;
51+
} catch(Exception x) {
52+
trx.rollback(); <8>
53+
throw x;
54+
} finally {
55+
em.close(); <9>
56+
}
57+
});
58+
}
59+
----
60+
61+
.Kotlin
62+
[source, kt, role="secondary"]
63+
----
64+
import io.jooby.hikari.Hikari
65+
66+
{
67+
install(Hikari()) <1>
68+
69+
install(new Hibernate()) <2>
70+
71+
get("/") {
72+
val em = require(EntityManager::class) <3>
73+
val trx = em.getTransaction() <4>
74+
try {
75+
trx.begin() <5>
76+
77+
// work with EntityManager compute a result <6>
78+
79+
trx.commit() <7>
80+
81+
result
82+
} catch(Exception x) {
83+
trx.rollback() <8>
84+
throw x
85+
} finally {
86+
em.close() <9>
87+
}
88+
}
89+
}
90+
----
91+
92+
<1> Install and creates a `DataSource`
93+
<2> Install and initializes Hibernate. Entities are automatically detected
94+
<3> Get a new `EntityManager`
95+
<4> Creates a new transaction
96+
<5> Being the transaction
97+
<6> Work with EntityManager (read, write to dababase)
98+
<7> Commit the transaction
99+
<8> Rollback transaction in case of error
100+
<9> Close the `EntityManager`
101+
102+
=== Entity Discovering
103+
104+
By default the javadoc:hibernate.Hbm[] module detects all the persistent entities under javadoc:Jooby[getBasePackage, text = "base/root package"]. The module provides two
105+
options for more explicit control:
106+
107+
- List persistent classes at creation time:
108+
109+
----
110+
install(new Hbm(MyEntity1.class, MyEntity2.class));
111+
----
112+
113+
- Explicit package scanning
114+
115+
----
116+
install(new Hbm().scan("mypackage"));
117+
----
118+
119+
=== Transactional Request
120+
121+
The javadoc:TransactionalRequest[] decorator takes care of a lifecycle of an `EntityManager` per HTTP request.
122+
The decorator creates, bind, begin/commit/rollback transaction and finally close it, so route handler
123+
doesn't have to deal with that boring lines of code.
124+
125+
.TransactionalRequest
126+
[source, java, role = "primary"]
127+
----
128+
import io.jooby.hikari.Hikari;
129+
import io.jooby.hibernate.Hbm;
130+
import io.jooby.hibernate.TransactionalRequest;
131+
132+
{
133+
install(new Hikari());
134+
135+
install(new Hbm());
136+
137+
decorator(new TransactionalRequest());
138+
139+
post("/create", ctx -> {
140+
EntityManager em = require(EntityManager.class);
141+
142+
MyEntity e = ...;
143+
144+
em.persist(e);
145+
146+
return e;
147+
});
148+
}
149+
----
150+
151+
.Kotlin
152+
[source, kt, role="secondary"]
153+
----
154+
import io.jooby.hikari.Hikari
155+
import io.jooby.hibernate.Hbm
156+
import io.jooby.hibernate.TransactionalRequest
157+
158+
{
159+
install(Hikari())
160+
161+
install(Hbm())
162+
163+
decorator(TransactionalRequest())
164+
165+
post("/create") { ctx ->
166+
val em = require(EntityManager::class)
167+
168+
val e = ...
169+
170+
em.persist(e)
171+
172+
e
173+
}
174+
}
175+
----
176+
177+
The `EntityManager` is tied to the current HTTP request. Multiple `require`/`injection` calls produce
178+
the same `EntityManager`. It is a simple way of managed simple read/write operations.
179+
180+
[NOTE]
181+
====
182+
The javadoc:TransactionalRequest[] doesn't extend session to the rendering phase (json, html, etc.).
183+
The route handler needs to make sure all the information required by the rendering phase is available.
184+
Otherwise, you are going to see `LazyInitializationException`.
185+
====
186+
187+
=== Schema Creation
188+
189+
Schema creation is controlled by the `hibernate.hbm2ddl.auto` property. The Hbm module configure this property using the following rules:
190+
191+
- When the javadoc:flyway.Flywayby[] module is present the value of `hibernate.hbm2ddl.auto` is set to `none`
192+
- When running on `dev` or `test` mode the value of `hibernate.hbm2ddl.auto` is set to `update`
193+
- Otherwise is set to `none`
194+
195+
=== Advanced Options
196+
197+
Advanced Hibernate configuration is supported from application configuration properties.
198+
199+
.application.conf
200+
[source, properties]
201+
----
202+
hibernate.hbm2ddl.auto = create
203+
----

docs/src/main/java/io/jooby/adoc/JavadocProcessor.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,13 @@ public Object process(ContentNode parent, String clazz, Map<String, Object> attr
7676
}
7777
}
7878
link.append("-");
79-
text.append(")");
79+
String label = (String) attributes.get("text");
80+
if (label != null) {
81+
text.setLength(0);
82+
text.append(label);
83+
} else {
84+
text.append(")");
85+
}
8086
} else if (variable != null) {
8187
link.append("#").append(variable);
8288
text.append(attributes.getOrDefault("text", Optional.ofNullable(arg1).orElse(classname)));

examples/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@
3939
<artifactId>jooby-hikari</artifactId>
4040
<version>${jooby.version}</version>
4141
</dependency>
42+
<dependency>
43+
<groupId>io.jooby</groupId>
44+
<artifactId>jooby-hibernate</artifactId>
45+
<version>${jooby.version}</version>
46+
</dependency>
4247
<dependency>
4348
<groupId>ch.qos.logback</groupId>
4449
<artifactId>logback-classic</artifactId>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package examples;
7+
8+
import examples.jpa.Person;
9+
import io.jooby.Jooby;
10+
import io.jooby.hibernate.Hbm;
11+
import io.jooby.hibernate.TransactionalRequest;
12+
import io.jooby.hikari.Hikari;
13+
import io.jooby.json.Jackson;
14+
15+
import javax.persistence.EntityManager;
16+
import java.util.List;
17+
18+
public class HibernateApp extends Jooby {
19+
{
20+
install(new Hikari("mem"));
21+
install(new Hbm());
22+
install(new Jackson());
23+
24+
decorator(new TransactionalRequest());
25+
26+
get("/create", ctx -> {
27+
EntityManager em = require(EntityManager.class);
28+
Person p = new Person();
29+
em.persist(p);
30+
return p;
31+
});
32+
33+
get("/people", ctx -> {
34+
35+
EntityManager em = require(EntityManager.class);
36+
37+
List<Person> people = em.createQuery("from Person", Person.class).getResultList();
38+
39+
return people;
40+
});
41+
}
42+
43+
public static void main(String[] args) {
44+
runApp(args, HibernateApp::new);
45+
}
46+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package examples.jpa;
7+
8+
import javax.persistence.Entity;
9+
import javax.persistence.GeneratedValue;
10+
import javax.persistence.Id;
11+
12+
@Entity
13+
public class Person {
14+
@Id
15+
@GeneratedValue
16+
private Long id;
17+
18+
private String fooBar;
19+
20+
public Long getId() {
21+
return id;
22+
}
23+
24+
public void setId(Long id) {
25+
this.id = id;
26+
}
27+
28+
public String getFooBar() {
29+
return fooBar;
30+
}
31+
32+
public void setFooBar(String fooBar) {
33+
this.fooBar = fooBar;
34+
}
35+
}

jooby/src/main/java/io/jooby/Context.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ public interface Context extends Registry {
7171
return getRouter().require(type);
7272
}
7373

74+
@Nonnull @Override default <T> T require(@Nonnull ServiceKey<T> key) throws RegistryException {
75+
return getRouter().require(key);
76+
}
77+
7478
/**
7579
* Context attributes (a.k.a request attributes).
7680
*

jooby/src/main/java/io/jooby/Environment.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.jooby;
77

88
import com.typesafe.config.Config;
9+
import com.typesafe.config.ConfigException;
910
import com.typesafe.config.ConfigFactory;
1011
import com.typesafe.config.ConfigParseOptions;
1112

@@ -52,7 +53,8 @@ public class Environment {
5253
* @param conf Application configuration.
5354
* @param actives Active environment names.
5455
*/
55-
public Environment(@Nonnull ClassLoader classLoader, @Nonnull Config conf, @Nonnull String... actives) {
56+
public Environment(@Nonnull ClassLoader classLoader, @Nonnull Config conf,
57+
@Nonnull String... actives) {
5658
this(classLoader, conf, Arrays.asList(actives));
5759
}
5860

@@ -63,7 +65,8 @@ public Environment(@Nonnull ClassLoader classLoader, @Nonnull Config conf, @Nonn
6365
* @param conf Application configuration.
6466
* @param actives Active environment names.
6567
*/
66-
public Environment(@Nonnull ClassLoader classLoader, @Nonnull Config conf, @Nonnull List<String> actives) {
68+
public Environment(@Nonnull ClassLoader classLoader, @Nonnull Config conf,
69+
@Nonnull List<String> actives) {
6770
this.classLoader = classLoader;
6871
this.actives = actives.stream()
6972
.map(String::trim)
@@ -80,10 +83,14 @@ public Environment(@Nonnull ClassLoader classLoader, @Nonnull Config conf, @Nonn
8083
* @return Property or default value.
8184
*/
8285
public @Nonnull String getProperty(@Nonnull String key, @Nonnull String defaults) {
83-
if (conf.hasPath(key)) {
84-
return conf.getString(key);
86+
try {
87+
if (conf.hasPath(key)) {
88+
return conf.getString(key);
89+
}
90+
return defaults;
91+
} catch (ConfigException x) {
92+
return defaults;
8593
}
86-
return defaults;
8794
}
8895

8996
/**
@@ -93,10 +100,14 @@ public Environment(@Nonnull ClassLoader classLoader, @Nonnull Config conf, @Nonn
93100
* @return Property value or <code>null</code> when missing.
94101
*/
95102
public @Nullable String getProperty(@Nonnull String key) {
96-
if (conf.hasPath(key)) {
97-
return conf.getString(key);
103+
try {
104+
if (conf.hasPath(key)) {
105+
return conf.getString(key);
106+
}
107+
return null;
108+
} catch (ConfigException x) {
109+
return null;
98110
}
99-
return getProperty(key, null);
100111
}
101112

102113
/**

0 commit comments

Comments
 (0)