Read-Only Views: @entity
Read-Only Views: @entity
1. Read-only Views
Views are another database feature you can easily use with
Hibernate. You can map a view in the same way as any database
table. As long as you follow the default naming strategy, you just need
a class with an @Entity annotation and an attribute for each database
column.
You can map read-only view in the same way. You just need an
additional annotation to tell Hibernate that it should ignore the
entity for all write operations. You can do that with Hibernate’s
@Immutable annotation.
@Entity
@Immutable
public class BookView { … }
www.thoughts-on-java.org
8 Ways to use the features of your database
Function Description
current_timestamp() Returns a timestamp of the
current date and time of the
database
substring(String s, int offset, Returns a substring of the given
int length) String s
trim(String s) Removes leading and trailing
whitespaces from the given
String s
length(String s) Returns the length of the given
String s
locate(String search, String s, Returns the position of the
int offset) String search in s. The search
starts at the position offset
abs(Numeric n) Returns the absolute value of the
given number
sqrt(Numeric n) Returns the square root of the
given number
mod(Numeric dividend, Returns the remainder of a
Numeric divisor) division
You can use these functions in the SELECT and WHERE clause of
your query. You can see a simple example in the following code
snippet.
www.thoughts-on-java.org
8 Ways to use the features of your database
I use the function function in the following code snippet, to call the
user-defined function calculate with the price of the book and a bind
parameter as arguments.
TypedQuery<Book> q = em.createQuery(
"SELECT b FROM Book b WHERE :double2 > "
"function('calculate', b.price, :double1)"
, Book.class);
3. Stored Procedures
Stored procedures provide another option to perform logic within
your database. That can be beneficial, if you need to share logic
between multiple applications that use the same database or if you’re
looking for the most efficient way to implement data-heavy
operations.
As you can see in the following code snippets, the annotation-based
definition of a stored procedure call isn’t complicated. In the first
step, you define the stored procedure call with a
@NamedStoredProcedure annotation by providing the name of the
stored procedure and its input and output parameters.
www.thoughts-on-java.org
8 Ways to use the features of your database
@NamedStoredProcedureQuery(
name = "calculate",
procedureName = "calculate",
parameters = {
@StoredProcedureParameter(
mode = ParameterMode.IN,
type = Double.class, name = "x"),
@StoredProcedureParameter(
mode = ParameterMode.IN,
type = Double.class, name = "y"),
@StoredProcedureParameter(
mode = ParameterMode.OUT,
type = Double.class, name = "sum")
}
)
www.thoughts-on-java.org
8 Ways to use the features of your database
StoredProcedureQuery query = this.em
.createNamedStoredProcedureQuery("calculate");
query.setParameter("x", 1.23d);
query.setParameter("y", 4.56d);
query.execute();
Double sum =
(Double) query.getOutputParameterValue("sum");
www.thoughts-on-java.org
8 Ways to use the features of your database
@Entity
public class Author {
@Column
@Generated(GenerationTime.ALWAYS)
private LocalDateTime lastUpdate;
…
}
www.thoughts-on-java.org
8 Ways to use the features of your database
@Entity
public class Author {
@Column
private LocalDate dateOfBirth;
…
}
6. Sequences
Database sequences are often used to generate unique primary key
values. Hibernate and JPA support different options to generate
primary key values and database sequences are, of course, one of
them.
If you want to use Hibernate’s default sequence, you just need to
annotate your primary key attribute with @GeneratedValue and set
the strategy to GenerationType.SEQUENCE.
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
You can also use a custom database sequence when you add a
@SequenceGenerator annotation. It allows you to define the name
www.thoughts-on-java.org
8 Ways to use the features of your database
and database schema of your sequence and the allocation size
Hibernate shall use to retrieve primary key values.
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "book_generator")
@SequenceGenerator(name="book_generator",
sequenceName = "book_seq", allocationSize=50)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false, nullable = false)
private Long id;
www.thoughts-on-java.org
8 Ways to use the features of your database
8. Custom and Database-Specific Datatypes
Most databases support a set of custom data types, like PostgreSQL’s
JSONB. JPA and Hibernate don’t support them. But that doesn’t mean
that you can’t use them. You just have to implement the mapping
yourself.
With Hibernate’s UserType interface, that is not as complicated as it
sounds. Let’s have a quick look at the most important steps. If you
want to dive deeper into this topic, please take a look at my post How
to use PostgreSQL’s JSONB data type with Hibernate.
Hibernate’s UserType interface allows you to define the mapping
between any Java type and any supported JDBC data type. That
requires the implementation of several methods. The 2 most
important ones are nullSafeGet and nullSafeSet. They implement the
mapping from the JDBC to the Java type and vice versa.
The following code snippet shows the implementation of these
methods for a UserType which maps a Java class to a JSONB database
column.
www.thoughts-on-java.org
8 Ways to use the features of your database
@Override
public Object nullSafeGet(final ResultSet rs,
final String[] names, final SessionImplementor session,
final Object owner)
throws HibernateException, SQLException {
@Override
public void nullSafeSet(final PreparedStatement ps, final
Object value, final int idx,
final SessionImplementor session) throws
HibernateException, SQLException {
if (value == null) {
www.thoughts-on-java.org
8 Ways to use the features of your database
@Override
public void nullSafeSet(final PreparedStatement ps,
final Object value, final int idx,
final SessionImplementor session)
throws HibernateException, SQLException {
if (value == null) {
ps.setNull(idx, Types.OTHER);
return;
}
try {
final ObjectMapper mapper = new ObjectMapper();
final StringWriter w = new StringWriter();
mapper.writeValue(w, value);
w.flush();
ps.setObject(idx, w.toString(), Types.OTHER);
} catch (final Exception ex) {
throw new RuntimeException(
"Failed to convert Invoice to String: " +
ex.getMessage(), ex);
}
}
After you implemented your own UserType, you need to register it.
You can do that with a @TypeDef annotation which you should add
to the package-info.java file.
www.thoughts-on-java.org
8 Ways to use the features of your database
@org.hibernate.annotations.TypeDef(name = "MyJsonType",
typeClass = MyJsonType.class)
package org.thoughts.on.java.model;
public MyPostgreSQL94Dialect() {
this.registerColumnType(Types.JAVA_OBJECT,
"jsonb");
}
}
www.thoughts-on-java.org