Loading...

Follow Thoughts on Java | Java Language Writing Blog on Feedspot

Continue with Google
Continue with Facebook
or

Valid

The post Hibernate Tip: How does Hibernate’s native ID generator work appeared first on Thoughts on Java.

Hibernate Tips is a series of posts in which I describe a quick and easy solution for common Hibernate questions. If you have a question for a future Hibernate Tip, please post a comment below.

Question:

One of the readers of my article about using Hibernate with a MySQL database asked the following question:

What is the difference between the following two recommended approaches concerning the primary keys?

@GeneratedValue(strategy = GenerationType.IDENTITY)
@GeneratedValue(generator = "native")
@GenericGenerator(name = "native", strategy = "native")

Solution:

As so often in software development, the answer to that question is: “It depends …”. In this case, it depends on the Dialect that you configured in your persistence.xml.

The native strategy

When you use a @GenericGenerator that references the native strategy, Hibernate uses the strategy natively supported by the configured Dialect. You can find the corresponding code in the of the Dialect class. Here’s is the code that’s used in Hibernate 5.4.

public String getNativeIdentifierGeneratorStrategy() {
	if ( getIdentityColumnSupport().supportsIdentityColumns() ) {
		return "identity";
	}
	else {
		return "sequence";
	}
}

For all commonly used databases, except MySQL, this method returns the String “sequence”. If you’re using a MySQL dialect, it returns “identity”.

Using the native strategy with different dialects

Let’s use the following, simple entity with a MySQL and a PostgreSQL database.

@Entity
public class Author {

	@Id
	@GeneratedValue(generator = "native")
	@GenericGenerator(name = "native", strategy = "native")
	private Long id;
	
	@Version
	private int version;

	private String firstName;

	private String lastName;
	
	...

}

When you persist a new Author entity using the PostgreSQL dialect, you can see that Hibernate uses the sequence native to generate the primary key value. And in the next step, it inserts the new record.

14:03:27,709 DEBUG [org.hibernate.SQL] - 
    select
        nextval ('native')
14:03:27,742 INFO  [org.thoughts.on.java.model.TestIdentityStrategy] - After persist
14:03:27,758 DEBUG [org.hibernate.SQL] - 
    insert 
    into
        Author
        (firstName, lastName, version, id) 
    values
        (?, ?, ?, ?)

If you use a MySQL database instead, you can see that Hibernate utilizes an auto-incremented column instead.

14:05:15,739 DEBUG [org.hibernate.SQL] - 
    insert 
    into
        Author
        (firstName, lastName, version) 
    values
        (?, ?, ?)
14:05:15,760 DEBUG [org.hibernate.id.IdentifierGeneratorHelper] - Natively generated identity: 1
The differences between native and identity strategy

So, as long as you use both mappings with a MySQL database, the result will be the same. But there are still a few differences:

  1. The behavior of the native strategy changes if your database dialect returns a different strategy as the natively supported one. That could happen because you now use a different database dialect or the internal implementation of the dialect changed.
  2. The @GenericGenerator mapping is much harder to read because it depends on the implementation of your database dialect.
  3. The @GenericGenerator annotation is Hibernate-specific. So, you can’t use this mapping with any other JPA implementation.
Mapping Recommendations

If you’ve read some of my other tutorials, you can probably guess my preferred mapping. I strongly recommend using the IDENTITY mapping instead of the native one. There are multiple reasons for that. The most obvious ones are the better readability and better portability to other JPA implementations.

But you also need to keep in mind that the generation of primary keys can have a significant impact on the performance of your application. I, therefore, want to define the strategy explicitly and don’t rely on any internal implementations.

I don’t see the adaptability of the native strategy to different database dialects as a huge advantage. For almost all dialects, Hibernate uses the SEQUENCE strategy anyways. And if you need to support MySQL databases, you can easily override the primary key strategy in your mapping file.

Get this Hibernate Tip as a printable PDF!Join the free Thoughts on Java Library to get access to lots of member-only content, like a printable PDF for this post, lots of cheat sheets and 2 ebooks about Hibernate.

Already a member? Login here.
Learn more:

If you are interested in primary key mappings, you should also read the following articles:

 
Hibernate Tips Book
Get more recipes like this one in my new book Hibernate Tips: More than 70 solutions to common Hibernate problems.

It gives you more than 70 ready-to-use recipes for topics like basic and advanced mappings, logging, Java 8 support, caching and statically and dynamically defined queries.

Get it now as a paperback, ebook or PDF.

The post Hibernate Tip: How does Hibernate’s native ID generator work appeared first on Thoughts on Java.

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The post Hibernate Tip: How to lazily load one-to-one associations appeared first on Thoughts on Java.

Hibernate Tips is a series of posts in which I describe a quick and easy solution for common Hibernate questions. If you have a question for a future Hibernate Tip, please post a comment below.

Question:

I modeled a one-to-one association and activated lazy loading for it. But it doesn’t work. How do I tell Hibernate to fetch a one-to-one association lazily?

Solution:

Configuring lazy loading for one-to-one associations is not as easy as it is for other associations. For all other association types, you just need to set the FetchType to FetchType.LAZY. Hibernate will then wait for you to use the relationship before it loads the associated entities.

Unfortunately, that’s not the case for one-to-one associations. It also depends on the mapping of the relationship and the Hibernate version you are using.

Most common mapping and its problems

A lot of developers model a one-to-one association using independent primary key columns for both tables and an additional foreign key column in one of the tables. That not only requires an additional database column; it also negatively affects your entity mapping. I will show you a more efficient way to model the association at the end of this article.

The entity that represents the table containing the foreign key column is called the owning side of the association. On this entity, Hibernate supports lazy loading as expected. You just have to set the fetch attribute of the @OneToOne association to FetchType.LAZY.

@Entity
public class Manuscript {

	@Id
	@GeneratedValue
	private Long id;
	
	private byte[] file;
	
	@OneToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "fk_book")
	private Book book;

	...
}

But if you model this as a bidirectional association, you will quickly recognize that Hibernate always fetches the other end of the association eagerly.

@Entity
public class Book {

	@Id
	@GeneratedValue
	private Long id;

	@OneToOne(mappedBy = "book", fetch = FetchType.LAZY)
	private Manuscript manuscript;

	...
}

That’s because Hibernate needs to know if it shall initialize the manuscript attribute with null or a proxy class. It can only find that out, by querying the manuscript table to find a record that references this Book entity. The Hibernate team decided that if they have to query the manuscript table anyways, it’s best to fetch the associated entity eagerly.

With some Hibernate versions, you can set the optional attribute of the @OneToOne annotation to false to avoid the eager fetching. Hibernate then always initializes the manuscript attribute with a proxy object. Unfortunately, this doesn’t work for all Hibernate version. If it works with your Hibernate version, you need to be prepared that it might change with future updates.

@Entity
public class Book {

	@Id
	@GeneratedValue
	private Long id;

	@OneToOne(mappedBy = "book", fetch = FetchType.LAZY, optional = false)
	private Manuscript manuscript;

	...
}
Most efficient mapping of a one-to-one association

You can avoid all these problems and get rid of the foreign key column by using the same primary key value for both associated entities. You can do that by annotating the owning side of the association with @MapsId.

@Entity
public class Manuscript {

	@Id
	private Long id;

	@OneToOne
	@MapsId
	@JoinColumn(name = "id")
	private Book book;

	...
}

The shared primary key allows you to model the relationship as a unidirectional one. You no longer need the referencing side of the association. If you have a Book entity object, you also know the primary key value of the associated Manuscript entity. So, you can simply use the find method on your EntityManager to fetch the Manuscript.

Book b = em.find(Book.class, 100L);
Manuscript m = em.find(Manuscript.class, b.getId());
Get this Hibernate Tip as a printable PDF!Join the free Thoughts on Java Library to get access to lots of member-only content, like a printable PDF for this post, lots of cheat sheets and 2 ebooks about Hibernate.

Already a member? Login here.
Learn more:

If you are interested in one-to-one association mappings, you should also read the following articles:

 
Hibernate Tips Book
Get more recipes like this one in my new book Hibernate Tips: More than 70 solutions to common Hibernate problems.

It gives you more than 70 ready-to-use recipes for topics like basic and advanced mappings, logging, Java 8 support, caching and statically and dynamically defined queries.

Get it now as a paperback, ebook or PDF.

The post Hibernate Tip: How to lazily load one-to-one associations appeared first on Thoughts on Java.

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The post Announcing 2 In-Person Workshops in December appeared first on Thoughts on Java.

I was asked several times when I will offer the next in-person workshop of my Advanced Hibernate Online Training or the Hibernate Performance Tuning Online Training. In the past, I had teamed up with training companies or conference organizers to host these events. They organized the location, signups, etc. and I taught the workshop. That worked great.

But I always thought that it would be better to host the workshops myself. That would allow me to bundle them with my online courses and to make them available to a broader audience by teaching them in English (please, don’t ask why I couldn’t do that before …) and offering them at a slightly lower price.

Over the last several weeks, my wife and I worked hard to find the right location and to organize everything. And today is the day that I can finally announce my first self-hosted workshops!

In the first week of December, I will teach the Advanced Hibernate Workshop (2-days) and the Hibernate Performance Tuning Workshop (3 days) in Düsseldorf (Germany). I will teach both of them in English, and the Airport Düsseldorf (DUS) offers non-stop connections to most bigger European airports. So, if you’re living in Europe, these workshops are for you!

And if you signup before August 23rd or bring your co-workers with you, I’m offering some sweet discounts. Find out more at Advanced Hibernate Workshop and Hibernate Performance Tuning Workshop.

Each workshop comes with an extensive handout and example projects, lots of exercises with solutions, a participation certificate, access to the corresponding online course and, of course, catering (incl. lunch, coffee breaks with snacks and drinks during the workshop). You just have to organize your travel and make sure to bring your laptop.

Advanced Hibernate Workshop (December 2nd-3rd)

In the Advanced Hibernate Workshop, we will focus on JPA and Hibernate features that enable you to map complex table and domain models. I will also show you several features that make the implementation of complex business requirements much easier.

During these 2 days, you will learn to:

  • use JPA’s and Hibernate’s advanced mappings
  • create dynamic, type-safe queries
  • implement lifecycle callbacks and entity listeners
  • support custom data types
  • manage concurrency
  • create a persistence layer for a multi-tenant application
  • utilize Hibernate-specific query features
  • avoid common anti-patterns by picking the right pattern for your use case

And I want to be sure that you will able to apply all the things you’ve learned to your own projects.

You will, therefore, spend ~50% of the time working on practical exercises and discussing their solutions. So, please make sure to bring a laptop.

And you also get access to the Advanced Hibernate Online Training. Whenever you don’t exactly remember how to use one of the features I showed you in the workshop, you can rewatch the corresponding lecture in the online course.

Sounds interesting? You can find all the details and a form to enroll in the workshop at https://thoughts-on-java.org/workshop-ah/.

Make sure to signup before August 23rd to get the best price.

“Great hands-on workshop about Hibernate’s advanced features.”
Gregor Karl Frey – Chief Development Architect at SAP

Hibernate Performance Tuning Workshop (December 4th-6th)

In the Hibernate Performance Tuning Workshop, you will learn how to identify performance problems before they cause trouble in production and how to fix them.

During this 3-day workshop, you will learn to:

  • understand typical reasons for performance problems
  • identify existing and potential issues in your persistence layer
  • improve the performance of your read operations
  • prevent duplicate read operations
  • move parts of your logic to the database (and when to avoid it)
  • speed-up write operations
  • optimize your concurrency management for performance

As in all of my workshops, I want to be sure that you can apply your new knowledge to your own projects.

That’s why you will spend about half of the time working on lots of practical exercises. Please make sure to bring your laptop.

And you will also get access to the Hibernate Performance Tuning Online Training so that you can re-watch a lecture whenever necessary.

Sounds interesting? You can find all the details and a form to enroll in the workshop at https://thoughts-on-java.org/workshop-hpt/.

Make sure to signup before August 23rd to get the best price.

“We doubled the performance of our service.”
Mike Hills – Senior Microservice Architect

I hope to see you at one of the workshops!

Thorben

The post Announcing 2 In-Person Workshops in December appeared first on Thoughts on Java.

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The post Mapping BLOBs and CLOBs with Hibernate and JPA appeared first on Thoughts on Java.

Databases use the data types BLOB (binary large object) and CLOB (character large object) to store large objects, like images and very long texts. JPA and Hibernate provide two kinds of mappings for these types.

You can choose if you want to:

  1. Materialize the LOB and map it to a byte[] or a String. This mapping is defined by the JPA specification and prioritizes ease of use over performance.
  2. Use JDBC’s LOB locators java.sql.Blob and java.sql.Clob. The LOB locators enable your JDBC driver to optimize for performance, e.g., by streaming the data. This mapping is Hibernate-specific.

The mapping of both approaches looks almost identical. You just need to annotate your entity attribute with a @Lob annotation. The only difference is the type of your entity attribute.

But you will see a huge difference in the way you use the values of these entity attributes.

Let’s use both options to map the following Book table.

The columns cover of type oid and content of type text are the important ones for this article. We will map the cover column as BLOB and the content column as a CLOB.

Mapping a LOB to String or byte[]

The materialized mapping to a String or a byte[] is the most intuitive mapping for most Java developers. Entity attributes of these types are easy to use, and it feels natural to use them in your domain model.

But Hibernate also needs to fetch all data stored in the LOB immediately and map it to a Java object. Depending on the size of your LOB, this can cause severe performance problems. If you, e.g., store large video files in your database, it’s often better to use JDBC’s LOB locators. I show you how to use them in the next section.

The JPA specification defines this mapping. You can not only use it with Hibernate but also with EclipseLink and OpenJPA.

Define the mapping

Creating materialized mappings is very simple. You just need an attribute of type String or byte[] and annotate it with JPA’s @Lob annotation.

@Entity
public class Book {

	@Id
	@GeneratedValue
	private Long id;

	private String title;
	
	@Lob
	private String content;
	
	@Lob
	private byte[] cover;

	...
}

Hibernate can also map nationalized character data types, like NCHAR, NVARCHAR, LONGNVARCHAR, and NCLOB. To define such a mapping, you need to annotate your entity attribute of type String with Hibernate’s @Nationalized annotation instead of @Lob.

@Entity
public class Book {

	@Id
	@GeneratedValue
	private Long id;

	private String title;
	
	@Nationalized
	private String content;

	...
}


Already a member? Login here.

Use the mapping

As I said at the beginning of this article, materialized mappings are straightforward to use. Hibernate fetches all data stored in the LOB when it initializes the entity and maps it to a String or byte[]. You can then use the entity attribute in the same way as any other attribute.

Here are 2 examples that store a new Book entity and fetch an existing Book entity from the database.

Book b = new Book();
b.setTitle("Hibernate Tips - More than 70 solutions to common Hibernate problems");
b.setCover(getCover());
b.setContent("This is a veeeery loooong text with almost all the content that you can find in the book ;)");
em.persist(b);
Book b2 = em.find(Book.class, b.getId());
byte[] cover = b2.getCover();
log.info(b2.getContent());
Mapping a LOB to java.sql.Clob or java.sql.Blob

With Hibernate, you can use the same approach to map your LOB to a java.sql.Clob or a java.sql.Blob. These Java types are not as easy to use as a String or byte[]. But they enable your JDBC driver to use LOB-specific optimizations, which might improve the performance of your application. If and what kind of optimizations are used, depends on the JDBC driver and your database.

The mapping is Hibernate-specific and not defined by the JPA specification.

Define the mapping

As you can see in the following code snippet, the mapping to JDBC’s LOB locators java.sql.Clob and java.sql.Blob is almost identical to the previous example. The only 2 differences are:

  1. The cover attribute is now of type Blob.
  2. The content attribute is of type Clob.
@Entity
public class Book {

	@Id
	@GeneratedValue
	private Long id;

	private String title;

	@Lob
	private Clob content;

	@Lob
	private Blob cover;

	...
}

And Hibernate also enables you to map the nationalized character data types NCHAR, NVARCHAR, LONGNVARCHAR, and NCLOB to a java.sql.Clob.

@Entity
public class Book {

	@Id
	@GeneratedValue
	private Long id;

	@Nationalized
	private Clob content;

	...
}
Use the mapping

The types java.sql.Clob and java.sql.Blob provide more flexibility to the JDBC driver, but they are not as easy to use as a byte[] or a String.

You need to use Hibernate’s BlobProxy and ClobProxy classes to create a Blob or Clob. As you can see in the code, that’s a rather small inconvenience.

Book b = new Book();
b.setTitle("Hibernate Tips - More than 70 solutions to common Hibernate problems");
b.setCover(BlobProxy.generateProxy(getCover()));
b.setContent(ClobProxy.generateProxy("This is a veeeery loooong text with almost all the content that you can find in the book ;)"));
em.persist(b);

To create a Blob object, you can call the generateProxy method of the BlobProxy with a byte[] or an InputStream. And you can call the generateProxy method of the ClobProxy with a String or a Reader. That makes both proxies very comfortable to use.

Reading a Blob or a Clob is also not too complicated but requires a little more work than using a byte[] or a String. The java.sql.Blob interface provides you with multiple methods to get an InputStream or a byte[] of the BLOB value. And the java.sql.Clob interface defines various ways to get a Reader or a String of the CLOB value.

Book b2 = em.find(Book.class, b.getId());
Reader charStream = b2.getContent().getCharacterStream();
InputStream binaryStream = b2.getCover().getBinaryStream();
Lazy loading for LOBs

When we are talking about LOBs, we also need to talk about lazy loading. In most cases, LOBs require too much memory to fetch them eagerly every time you fetch the entity. It would be better only to fetch the LOB if you need it in your business code.

As I explained in a previous article, JPA defines lazy fetching for basic attributes as a hint. That means that your persistence provider can decide if it follows that hint or fetches the value eagerly.

As a result, the support and implementation of this feature depend on your JPA implementation. Hibernate, for example, requires you to activate byte code enhancement. I explain that in more details in my Hibernate Performance Tuning Online Training.

In this article, I want to show and recommend a different approach. It doesn’t require any provider-specific features and works with all JPA implementations.

The easiest and best way to load LOBs lazily is to store them in a separate table.

You can then map the LOBs to a separate entity.

That allows you to remove the LOBs from the Book entity and to model a unidirectional one-to-one association with a shared primary key on the BookLobs entity.

Define the mapping

The mapping of the 2 entities is pretty simple.

After removing the 2 LOBs, the Book entity is a simple entity with a generated primary key and a title attribute. As I will show you in the next section, you don’t need to model the association to the BookLob entity.

@Entity
public class Book {

	@Id
	@GeneratedValue
	private Long id;

	private String title;

	...
}

The BookLob entity models a unidirectional one-to-one association to the Book entity. The @MapsId annotation tells Hibernate to use the primary key value of the associated Book. I explained that in more details in Hibernate Tips: How to Share the Primary Key in a One-to-One Association.

And the materialized mappings of the content and cover attributes are the same as I used in the first example.

@Entity
public class BookLobs {

	@Id
	private Long id;
	
	@OneToOne
	@MapsId
	private Book book;
	
	@Lob
	private String content;
	
	@Lob
	private byte[] cover;

	...
}
Use the mapping

The shared primary key and the unidirectional one-to-one associations make using the 2 entities very easy.

To store a new book in your database, you need to instantiate and persist a Book and BookLobs entity. The BookLobs entity uses the primary key value of the associated Book entity. So, you need to make sure to initialize the association before you persist the BookLobs entity.

Book b = new Book();
b.setTitle("Hibernate Tips - More than 70 solutions to common Hibernate problems");
em.persist(b);

BookLobs bLob = new BookLobs();
bLob.setCover(getCover());
bLob.setContent("This is a veeeery loooong text with almost all the content that you can find in the book ;)");
bLob.setBook(b);
em.persist(bLob);

And when you want to get the BookLobs entity for a given Book entity, you just need to call the find method on your EntityManager with the id of the Book.

Book b2 = em.find(Book.class, b.getId());
BookLobs bLob2 = em.find(BookLobs.class, b2.getId());
byte[] cover = bLob2.getCover();
log.info(bLob2.getContent());


Already a member? Login here.

Conclusion

As you have seen, JPA provides an easy way to map LOBs to entity attributes of type byte[] and String. This mapping is not only easy to define, but it’s also very comfortable to use and feels natural to most Java developers. The only downside of it is that it prevents your JDBC driver from using LOB-specific optimizations.

Hibernate takes the mapping of LOBs one step further. In addition to the JPA mappings, it also allows you to map LOBs to JDBC’s Clob and Lob locators. These mappings are a little bit harder to use. But they enable your JDBC driver to implement LOB specific implementations, like the streaming of large objects. That can provide better performance if you’re using a lot of or very huge LOBs.

The post Mapping BLOBs and CLOBs with Hibernate and JPA appeared first on Thoughts on Java.

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The post Hibernate Tips: How to Prevent the Removal of a Parent Entity with Children appeared first on Thoughts on Java.

Hibernate Tips is a series of posts in which I describe a quick and easy solution for common Hibernate questions. If you have a question for a future Hibernate Tip, please post a comment below.

Question:

In one of the previous Hibernate Tips, I showed how to remove child entities automatically when you remove their parent. In the comments of that post, Jakob asked how to do the exact opposite. He wants to prevent the removal of entities that are referenced in an association:

“I am not allowed to delete a book that has a review. Is it possible to do that in Hibernate?”

Solution:

Yes, that’s possible. If you model the association on the Book entity, you can do that easily.

You can implement a lifecycle callback on the Book entity, that gets triggered before the entity gets removed. Within this method, you can access all attributes of the entity object. That enables you to check if the mapped association contains any elements. If it isn’t empty, you throw an exception to cancel the operation.

Another option would be to rely on a database constraint. You then don’t need to perform any validation in your Java application. This approach is efficient and easy to implement. But it also distributes the validation over multiple systems. That makes it harder to show a specific error message to the user.

Let’s assume that you want to perform all possible validations in your Java code. I nevertheless recommend adding the foreign key constraint on the database. You can then be absolutely sure that no referenced Book entity gets removed.

Mapping the Book entity

Here you can see a typical mapping of a Book entity.

@Entity
public class Book {

	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "book_seq")
	private Long id;

	@Version
	private int version;

	private String title;

	@OneToMany(mappedBy = "book")
	private List reviews = new ArrayList();

	...
	
	@PreRemove
	public void checkReviewAssociationBeforeRemoval() {
		if (!this.reviews.isEmpty()) {
			throw new RuntimeException("Can't remove a book that has reviews.");
		}
	}
}

The id attribute maps the primary key. The @GeneratedValue annotation tells Hibernate to use the sequence book_seq to generate primary key values. Since Hibernate 5.3, you can do that without specifying the @SequenceGenerator. Hibernate then uses a database sequence that has the same name as your generator.

The version attribute is used by JPA’s optimistic locking mechanism to prevent concurrent updates. I explain it in great details in my Advanced Hibernate Online Training.

The title attribute maps a simple String to a database column.

The reviews attribute models the referencing side of a bidirectional many-to-one association. You need this attribute to implement the check in your lifecycle callback method.

The most interesting part of this entity is the checkReviewAssociationBeforeRemoval method. I annotated it with a @PreRemove annotation. This annotation tells your persistence provider to call this method before calling the remove method on the EntityManager.

Within this method, you can access all attributes of the entity object. You can use that to check if the reviews attribute contains any Review objects. If it does, you throw an Exception to cancel the remove operation. Please keep in mind that Hibernate might need to perform a database query to retrieve the associated Review entities if it did not already fetch them.

In this example, I throw a standard RuntimeException. But you could, of course, use one of your own business exceptions.

Testing the Book mapping

Let’s use the following test case to check that the lifecycle callback works as expected.

I first persist a new Book entity with a Review.

When I then try to remove that Book entity, Hibernate calls the checkReviewAssociationBeforeRemoval before it executes the remove method of the EntityManager. The Book entity references one Review. We, therefore, expect that the checkReviewAssociationBeforeRemoval method throws an exception. This will prevent Hibernate from removing the Book entity.

// Persist Book with 1 Review
log.info("Persist Book with 1 Review");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

Book b = new Book();
b.setTitle("Hibernate Tips - More than 70 solutions to common Hibernate problems");
em.persist(b);

Review r = new Review();
r.setComment("Amazing book!");
r.setBook(b);
b.getReviews().add(r);
em.persist(r);

em.getTransaction().commit();
em.close();

// Try to remove Book
log.info("Try to remove Book");
em = emf.createEntityManager();
em.getTransaction().begin();

b = em.find(Book.class, b.getId());
try {
	em.remove(b);
	Assert.fail("RuntimeException expected - Books with reviews can't be removed");
} catch (RuntimeException e) {
	log.info("Caught expected exception: "+e);
}

When you activate the logging of SQL statements, you can see that the callback works as expected. The checkReviewAssociationBeforeRemoval method throws a RuntimeException, which prevents Hibernate from removing the Book entity.

07:41:26,982 INFO  [org.thoughts.on.java.model.TestBidirectionalOneToMany] - Persist Book with 1 Review
07:41:27,274 DEBUG [org.hibernate.SQL] - select nextval ('book_seq')
07:41:27,283 DEBUG [org.hibernate.SQL] - select nextval ('book_seq')
07:41:27,342 DEBUG [org.hibernate.SQL] - select nextval ('review_seq')
07:41:27,349 DEBUG [org.hibernate.SQL] - select nextval ('review_seq')
07:41:27,374 DEBUG [org.hibernate.SQL] - insert into Book (title, version, id) values (?, ?, ?)
07:41:27,383 DEBUG [org.hibernate.SQL] - insert into Review (fk_book, comment, id) values (?, ?, ?)
07:41:27,395 INFO  [org.thoughts.on.java.model.TestBidirectionalOneToMany] - Try to remove Book
07:42:49,786 DEBUG [org.hibernate.SQL] - select book0_.id as id1_0_0_, book0_.title as title2_0_0_, book0_.version as version3_0_0_ from Book book0_ where book0_.id=?
07:42:49,808 DEBUG [org.hibernate.SQL] - select reviews0_.fk_book as fk_book3_1_0_, reviews0_.id as id1_1_0_, reviews0_.id as id1_1_1_, reviews0_.fk_book as fk_book3_1_1_, reviews0_.comment as comment2_1_1_ from Review reviews0_ where reviews0_.fk_book=?
07:42:49,816 INFO  [org.thoughts.on.java.model.TestBidirectionalOneToMany] - Caught expected exception: java.lang.RuntimeException: Can't remove a book that has reviews.
Get this Hibernate Tip as a printable PDF!Join the free Thoughts on Java Library to get access to lots of member-only content, like a printable PDF for this post, lots of cheat sheets and 2 ebooks about Hibernate.

Already a member? Login here.
Learn more:

The following articles go into more details about JPA’s callback methods and other validation options:

 
Hibernate Tips Book
Get more recipes like this one in my new book Hibernate Tips: More than 70 solutions to common Hibernate problems.

It gives you more than 70 ready-to-use recipes for topics like basic and advanced mappings, logging, Java 8 support, caching and statically and dynamically defined queries.

Get it now as a paperback, ebook or PDF.

The post Hibernate Tips: How to Prevent the Removal of a Parent Entity with Children appeared first on Thoughts on Java.

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The post How to Use Named Queries with Spring Data JPA appeared first on Thoughts on Java.

Spring Data JPA provides various options to define and execute queries. All of them use JPA’s query capabilities but make them a lot easier to use. You can:

I already explained the 2nd and 3rd options in previous articles. In this tutorial, I want to focus on the first option: the referencing of a named native or JPQL query in a Spring Data JPA repository. It makes executing your query much easier because Spring Data takes care of all the boilerplate code required by JPA.

Defining a Named Query with JPA

Named queries are one of the core concepts in JPA. They enable you to declare a query in your persistence layer and reference it in your business code. That makes it easy to reuse an existing query. It also enables you to separate the definition of your query from your business code.

You can define a named query using a @NamedQuery annotation on an entity class or using a <named-query /> element in your XML mapping. In this article, I will show you the annotation-based mapping. It’s the by far the most common approach to creating a named query.

When you define a named query, you can provide a JPQL query or a native SQL query in very similar ways. Let’s take a look at both options.



Already a member? Login here.

Defining a Named JPL Query

The JPA specification defines its own query language. It’s called JPQL, and its syntax is similar to SQL. But there are 2 essential differences between these 2:

  1. You define your JPQL query based on your entity model. When you execute it, your persistence provider generates a SQL query based on your entity mappings and the provided JPQL statement. That enables you to define database-independent queries but also limits you to the features supported by your persistence provider.
  2. JPQL supports only a small subset of the SQL standard and almost no database-specific features.

The definition of a named JPQL query is pretty simple. You just have to annotate one of your entity classes with @NamedQuery and provide 2 Strings for the name and query attributes.

The name of your query has to be unique within your persistence context. You will use it in your business code or repository definition to reference the query.

The name doesn’t have to follow any conventions if you want to reference the query programmatically. But if you’re going to reference it in a Spring Data JPA repository, the name should start with the name of the entity class, followed by a “.” and the name of the repository method.

The value of the query attribute has to be a String that contains a valid JPQL statement. If your query returns an entity, you can define your projection implicitly, as you can see in the Author.findByFirstName query. The Author.findByFirstNameAndLastName query contains a SELECT clause to define the projection explicitly.

JPQL, of course, supports way more features than I use in these simple examples. You can learn more about it in my Ultimate Guide to JPQL Queries with JPA and Hibernate.

@Entity
@NamedQuery(name = "Author.findByFirstName", query = "FROM Author WHERE firstName = ?1")
@NamedQuery(name = "Author.findByFirstNameAndLastName", query = "SELECT a FROM Author a WHERE a.firstName = ?1 AND a.lastName = ?2")
public class Author { ... }

If you want to define multiple JPQL queries and use at least JPA 2.2 or Hibernate 5.2, you can annotate your class with multiple @NamedQuery annotations. If you are using an older JPA or Hibernate version, you need to wrap your @NamedQuery annotation within a @NamedQueries annotation.

Defining a Named Native Query
Watch it on YouTube

Follow me on YouTube to not miss any new videos.

Native SQL queries are more powerful and flexible than JPQL queries. Your persistence provider doesn’t parse these queries and sends them directly to the database. That enables you to use all SQL features supported by your database. But you also have to handle the different database dialects if you need to support multiple DBMS.

You can define a named native query in almost the same way as you specify a named JPQL query. The 3 main differences are:

  1. You need to use a @NamedNativeQuery instead of a @NamedQuery annotation.
  2. The value of the query attribute has to be an SQL statement instead of a JPQL statement.
  3. You can define an entity class or a reference to an @SqlResultSetMapping that will be used to map the result of your query. Spring Data JPA can provide a set of default mappings so that you often don’t need to specify it.

Here you can see the same queries as in the previous example. But this time, they are defined as native SQL queries instead of JPQL queries.

@Entity
@NamedNativeQuery(name = "Author.findByFirstName", query = "SELECT * FROM author WHERE first_name = ?", resultClass = Author.class)
@NamedNativeQuery(name = "Author.findByFirstNameAndLastName", query = "SELECT * FROM author WHERE first_name = ? AND last_name = ?", resultClass = Author.class)
public class Author { ... }
Executing a Named Query Programmatically with JPA

Using JPA’s EntityManager, you can run named native and named JPQL queries in the same way:

  1. You call the createNamedQuery method on the EntityManager with the name of the named query you want to execute. That gives you an instance of a Query or TypedQuery interface.
  2. You then call the setParameter method on the returned interface for each bind parameter used in your query.
  3. As a final step, you call the getSingleResult or getResultSet method on the Query or TypedQuery interface. That executes the query and returns 1 or multiple result set records.

Here you can see the required code to execute the Author.findByFirstName query that we defined in the 2 previous examples.

Query q = em.createNamedQuery("Author.findByFirstName");
q.setParameter(1, "Thorben");
List a = q.getResultList();

Before you run this code, you should activate the logging of SQL statements. You can then see the executed SQL statement and the used bind parameter values in your log file. In this example, I called the @NamedNativeQuery version of the previously shown Author.findByFirstName query.

2019-06-24 19:20:32.061 DEBUG 10596 --- [           main] org.hibernate.SQL                        : 
    SELECT
        * 
    FROM
        author 
    WHERE
        first_name = ?
2019-06-24 19:20:32.073 TRACE 10596 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [Thorben]
2019-06-24 19:20:32.116 TRACE 10596 --- [           main] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([id] : [NUMERIC]) - [1]
2019-06-24 19:20:32.118 TRACE 10596 --- [           main] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([first_name] : [VARCHAR]) - [Thorben]
2019-06-24 19:20:32.119 TRACE 10596 --- [           main] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([last_name] : [VARCHAR]) - [Janssen]
2019-06-24 19:20:32.121 TRACE 10596 --- [           main] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([version] : [INTEGER]) - [0]
Referencing a Named Query in a Spring Data JPA repository

As you have seen in the previous example, executing a named query using JPA’s EntityManager isn’t complicated, but it requires multiple steps.

Spring Data JPA takes care of that if you reference a named query in your repository definition. Doing that is extremely simple if you follow Spring Data’s naming convention. The name of your query has to start with the name of your entity class, followed by “.” and the name of your repository method.

In the previous examples, I defined the named queries Author.findByFirstName and Author.findByFirstNameAndLastName as JPQL and native queries. You can reference both versions of these queries by adding the methods findByFirstName and findByFirstNameAndLastName to the AuthorRepository.

public interface AuthorRepository extends JpaRepository<Author, Long> {

    List<Author> findByFirstName(String firstName);

    List<Author> findByFirstNameAndLastName(String firstName, String lastName);

}

You can then inject an AuthorRepository instance in your business code and call the repository methods to execute the named queries.

As you can see in the following code snippet, you can use these repository methods in the same way as a repository method that executes a derived query or a declared query. Spring Data JPA handles the instantiation of the named query, sets the bind parameter values, executes the query, and maps the result.

List<Author> a = authorRepository.findByFirstName("Thorben");


Already a member? Login here.

Conclusion

Named queries are one of the various options to query data from your database that are defined by the JPA specification.

Spring Data JPA provides a very comfortable way to use named queries in your repository. It takes care of all the repetitive boilerplate code required by the JPA specification. By default, Spring Data JPA checks for a named JPQL or a named native query that follows the naming convention <entity class name>.<repository method name> before it tries to derive a query from the method name.

The post How to Use Named Queries with Spring Data JPA appeared first on Thoughts on Java.

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The post 5 Primary Key Mappings for JPA and Hibernate Every Developer Should Know appeared first on Thoughts on Java.

Mapping a primary key column with JPA and Hibernate is simple. You just need to add an attribute to your entity, make sure that its type and name match the database column, annotate it with @Column and you’re done. You can then use the primary key to load the entity, and Hibernate sets the primary key value automatically. And if you want to persist a new entity, you need to set the primary key value programmatically.

But with JPA and Hibernate you can do much more than that. You can:

  • choose between different strategies to generate unique primary key values,
  • use UUIDs and generate their values,
  • map composite primary keys,
  • share primary key values across associations and
  • map natural IDs.

Generate Numeric Primary Key Values
Watch it on YouTube

Follow me on YouTube to not miss any new videos.

Most table models use simple, numerical primary keys. They are easy to use and very efficient at the same time.

You can either set their values programmatically or use one of JPA’s generation strategies to create them automatically. The easiest way to do that is to annotate your primary key attribute with a @GeneratedValue annotation. Hibernate will then pick a strategy based on the database-specific dialect.

@Entity
public class Book {
 
    @Id
    @GeneratedValue
    private Long id;
     
    …
}

Using the auto strategy, or not referencing a strategy at all, is the simplest but not the best way. It’s better to specify the strategy. You can choose between:

  • GenerationType.AUTO – Let Hibernate pick one of the following strategies.
  • GenerationType.SEQUENCE – Use a database sequence.
  • GenerationType.IDENTITY – Use an autoincremented database columns.
  • GenerationType.TABLE – Use a database table to simulate a sequence.

That ensures that a Hibernate update will not accidentally change your generation strategy and if you’re using the GenerationType.SEQUENCE, it will also activate Hibernate’s performance optimizations.

Defining the strategy is simple. You just need to provide it as the value of the strategy attribute of the @GeneratedValue annotation.

The following mapping tells Hibernate to use a database sequence to generate primary key values.

@Entity
public class Book {
 
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;
     
    …
}

By default, Hibernate uses a sequence called hibernate_sequence. You can also tell Hibernate to use one of your own database sequences. I explained that in more details in Hibernate Tips: How to use a custom database sequence.



Already a member? Login here.

Generate UUID Primary Keys
Watch it on YouTube

Follow me on YouTube to not miss any new videos.

UUIDs and numerical primary keys might seem very different. But with Hibernate, you can map and use them in almost the same way. The only difference is the type of the primary key attribute, which is a java.util.UUID instead of a java.lang.Long.

Here is a simple example. The Book entity maps an attribute of type UUID and uses one of Hibernate’s generators to create primary key values automatically before persisting a new entity.

@Entity
public class Book {

	@Id
	@GeneratedValue
	private UUID id;
	
	…
}

This is the easiest way to map and generate a UUID as a primary key. If you want to take a more detailed look at your mapping options, please read How to generate UUIDs as primary keys with Hibernate.

You can then use this Book entity in the same way as you would use an entity that maps a primary key attribute of type Long.

Book b = new Book();
b.setTitle(“Hibernate Tips - More than 70 solutions to common Hibernate problems”);
b.setPublishingDate(LocalDate.of(2017, 4, 4));
em.persist(b);

When Hibernate persists this Book entity, it first generates a UUID. It then sets that value as the id value in the SQL INSERT statement. You can see this in the log file if you activate my recommended development configuration.

12:23:19,356 DEBUG AbstractSaveEventListener:118 – Generated identifier: d7cd23b8-991c-470f-ac63-d8fb106f391e, using strategy: org.hibernate.id.UUIDGenerator
12:23:19,388 DEBUG SQL:92 – insert into Book (publishingDate, title, version, id) values (?, ?, ?, ?)
12:23:19,392 TRACE BasicBinder:65 – binding parameter [1] as [DATE] – [2017-04-04]
12:23:19,393 TRACE BasicBinder:65 – binding parameter [2] as [VARCHAR] – [Hibernate Tips - More than 70 solutions to common Hibernate problems]
12:23:19,393 TRACE BasicBinder:65 – binding parameter [3] as [INTEGER] – [0]
12:23:19,394 TRACE BasicBinder:65 – binding parameter [4] as [OTHER] – [d7cd23b8-991c-470f-ac63-d8fb106f391e]
Manage Composite Primary Keys
Watch it on YouTube

Follow me on YouTube to not miss any new videos.

JPA and Hibernate also provide multiple ways to map composite primary keys that consist of multiple attributes. Let’s take a look at my preferred option: the embedded id.

I explain this and all other options in great details in my Advanced Hibernate Online Training (enrollment opens next week).

The embedded id approach uses an embeddable to map the primary key attributes.

An embeddable is a pure Java class that is annotated with @Embeddable. It defines attribute mappings in a reusable way.

If you want to use it as an embedded id, you also need to implement the equals and hashCode methods.

@Embeddable
public class AddressKey implements Serializable {
 
    private Long xId;
    private Long yId;
     
    public AddressKey() {}
     
    public AddressKey(Long xId, Long yId) {
        super();
        this.xId = xId;
        this.yId = yId;
    }
 
    public Long getxId() {
        return xId;
    }
 
    public void setxId(Long xId) {
        this.xId = xId;
    }
 
    public Long getyId() {
        return yId;
    }
 
    public void setyId(Long yId) {
        this.yId = yId;
    }
 
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((xId == null) ? 0 : xId.hashCode());
        result = prime * result + ((yId == null) ? 0 : yId.hashCode());
        return result;
    }
 
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        AddressKey other = (AddressKey) obj;
        if (xId == null) {
            if (other.xId != null)
                return false;
        } else if (!xId.equals(other.xId))
            return false;
        if (yId == null) {
            if (other.yId != null)
                return false;
        } else if (!yId.equals(other.yId))
            return false;
        return true;
    }
}

You can then use the embeddable class as the type of your primary key attribute and annotate it with @EmbeddedId. The embeddable and all its attributes become part of the entity. It follows the same lifecycle, and all its attributes get mapped to the database table that’s mapped by the entity.

@Entity
public class Address {
 
    @EmbeddedId
    private AddressKey id;
 
    private String city;
 
    private String street;
 
    private String country;
 
    @OneToOne(mappedBy = "address")
    private Person person;
 
    ...
}

After you defined the mapping, you can easily use the embedded id to create a new or to fetch an existing entity.

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

Address a = new Address();
AddressKey aKey = new AddressKey(1L, 2L);
a.setId(aKey);
a.setCity("A City");
a.setCountry("A Country");
a.setStreet("A Street");
em.persist(a);

em.getTransaction().commit();
em.close();

em = emf.createEntityManager();
em.getTransaction().begin();

aKey = new AddressKey(1L, 2L);
a = em.find(Address.class, aKey);

em.getTransaction().commit();
em.close();
13:30:30,824 DEBUG [org.hibernate.SQL] - 
    insert 
    into
        Address
        (city, country, street, xId, yId) 
    values
        (?, ?, ?, ?, ?)
13:30:30,865 DEBUG [org.hibernate.SQL] - 
    select
        address0_.xId as xId1_0_0_,
        address0_.yId as yId2_0_0_,
        address0_.city as city3_0_0_,
        address0_.country as country4_0_0_,
        address0_.street as street5_0_0_ 
    from
        Address address0_ 
    where
        address0_.xId=? 
        and address0_.yId=?
Use Same Primary Key Values for Associated Entities

Another common primary key mapping is to use the same primary key value in a one-to-one association.

You can, of course, map this with JPA and Hibernate. The only things you need to do are to model the owning side of the association on the entity that shall reuse the primary key value and to add a @MapsId annotation to it.

@Entity
public class Manuscript {
 
    @Id
    private Long id;
     
    private byte[] file;
     
    @OneToOne
    @JoinColumn(name = "id")
    @MapsId
    private Book book;
     
    ...
}

When you persist the Manuscript entity, you only need to set the association to the Book entity. Hibernate will then use the primary key value of the Book for the new Manuscript.

Book b = em.find(Book.class, 1L);
         
Manuscript m = new Manuscript();
m.setBook(b);
 
b.setManuscript(m);
 
em.persist(m);

You can dive deeper into this mapping in Hibernate Tips: How to Share the Primary Key in a One-to-One Association.

Work with Natural ID

Most teams prefer to use a surrogate key as the primary key. It’s easier to manage in your code, and all involved systems can handle it more efficiently. But modeling a natural ID is still useful. You will, most likely, reference them very often in your use cases.

Hibernate provides an annotation to declare a natural ID and an API to retrieve entities by it. Let’s take a quick look at the most important details. And if you want to dive deeper, please read my article @NaturalId – A good way to persist natural IDs with Hibernate?

You can specify a natural ID by annotating one or more entity attributes with @NaturalId. I use it in the following code snippet, to tell Hibernate that the isbn attribute is a natural ID of the Book entity.

Entity
public class Book {

  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE)
  private Long id;

  @NaturalId
  private String isbn;

  …
}

After you’ve done that, you can use the byNaturalId method on Hibernate’s Session interface to create a query that loads an entity by its natural id. If you’re using JPA’s EntityManager, you can get the corresponding Session interface by calling the unwrap method.

In the next step, you need to provide the value of the natural id by calling the using method for each attribute that’s part of the natural id. In this example, the natural id only consists of the isbn attribute, which I reference using the JPA metamodel class of the Book entity.

And after you provided the natural id value, you can call the load method to execute the query.

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Session session = em.unwrap(Session.class);

Book b = session.byNaturalId(Book.class).using(Book_.isbn.getName(), “978-0321356680”).load();

When you run this code and activate the logging of SQL statements, you can see that Hibernate first gets the primary key value for the provided natural id. It then executes a second query to load the entity by its primary key. The result of the first query gets cached so that Hibernate doesn’t need to perform it again.

06:14:40,705 DEBUG SQL:92 – select book_.id as id1_0_ from Book book_ where book_.isbn=?
06:14:40,715 DEBUG SQL:92 – select book0_.id as id1_0_0_, book0_.isbn as isbn2_0_0_, book0_.publishingDate as publishi3_0_0_, book0_.title as title4_0_0_, book0_.version as version5_0_0_ from Book book0_ where book0_.id=?


Already a member? Login here.

Conclusion

JPA and Hibernate can do much more than just mapping a numerical primary key column to an entity attribute. You can use them to generate unique primary key values, to map and create UUIDs, to work with composite primary keys, and to use the same primary key value for associated entities. And Hibernate also supports natural primary keys with its own, proprietary query mechanism.

The post 5 Primary Key Mappings for JPA and Hibernate Every Developer Should Know appeared first on Thoughts on Java.

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The post Localized Data – How to Map It With Hibernate appeared first on Thoughts on Java.

Internationalization not only affects the UI. If your application stores user-generated data and supports multiple languages, you also need a way to store different translations in your database. Typical examples are:

  • market places that allow you to provide product descriptions in various languages,
  • travel sites that offer trips to people all over the world and
  • document management systems that store document descriptions and keywords for multiple languages.

In all of these examples, you need to localize your frontend and parts of the persisted data. The two most common approaches for that are:

  1. Using Java ResourceBundle
    This standard Java feature provides a simple to use and very efficient option to implement internationalization. You need to provide a properties file for each locale you want to support. You can then use the ResourceBundle class to get the property for the currently active Locale.
    The only downside of this approach is that the different translations are hard to maintain. If you want to add, change, or remove the translation of a property, you need to edit one or more properties files. In the worst case, that might even require a re-deployment of your application.
    That makes Java’s ResourceBundle a good option for all static, pre-defined texts, like general messages or attribute names that you use in your UI. But if you want to translate user-generated content or any other String that gets often changed, you should prefer a different approach.
  2. Storing translations in the database
    You get more flexibility, and updating a translated name or description is much easier if you persist the localized data in your database. Adding or changing a translation, then only requires the execution of an SQL INSERT or UPDATE statement. That makes it a great approach for all user-generated content.
    Unfortunately, the implementation is also more complicated. There is no standard Java feature that you can easily use. You need to design your table model accordingly, and you need to implement the read and update routines yourself.

In this tutorial, I want to focus on the 2nd option. There are a few commonly used patterns that enable you to store and handle localized information in your database easily.

Different Ways to Store Localized Data

Let’s first take a look at the table model before we discuss how you can map it with JPA and Hibernate. To make that easier to understand, I will use the following example:

We want to create a marketplace in which suppliers can offer their products. The marketplace supports the languages German and English. The supplier can provide the name and description of a product in both languages.

As so often, you can model this in various ways. Shantanu Kher created a great overview of different options and discussed their advantages and disadvantages on the vertabelo blog.

Even though the popularity of these approaches varies, I have seen all of them in real life. In my experience, the most commonly used ones are:

  1. Using separate columns for each language in the same database table, e.g., modeling the columns description_en and description_de to store different translations of a product description.
  2. Storing translated fields in a separate table. That would move the description_en and description_de columns to a different table. Let’s call it LocalizedProduct.

Let’s take a closer look at both options.



Already a member? Login here.

Separate Language Columns in Each Table

The general idea of this approach is simple. For each localized attribute and language you need to support, you add an extra column to your table. Depending on the number of supported languages and localized attributes, this can result in a vast amount of additional columns. If you want to translate 4 attributes into 5 different languages, you would need to model 4*5=20 database columns.

In the previously described example, you need 4 database columns to localize the product name and description. You use the columns description_en and description_de to persist the different translations of the product description. The columns name_en and name_de to store the localized product name.

Creating Your Entity Mappings

As you have seen in the previous diagram, using separate columns for each translation results in a straightforward table model. The same is true for the entity mapping.

Watch it on YouTube

Follow me on YouTube to not miss any new videos.

The id attribute is of type Long and maps the primary key. The @GeneratedValue annotation tells Hibernate to use a database sequence to generate unique primary key values. In this example, I use Hibernate’s default sequence. But as I showed in a previous article, you can easily provide your own sequence.

The version attribute is used for optimistic locking and provides a highly scalable way to avoid concurrent updates. I explain it in more details in my Hibernate Performance Tuning Online Training.

The supplier attribute defines the owning side of a many-to-one association to the Supplier entity. As for all to-one associations, you should make sure to set the FetchType to LAZY to avoid unnecessary queries and performance problems.

The nameDe, nameEn, descriptionDe, and descriptionEn attributes just map each of the localized columns. That might result in a lot of attributes, but it is also a simple and efficient way to handle localized data.

@Entity
public class Product {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;

	@Version
	private int version;

	@ManyToOne(fetch = FetchType.LAZY)
	private Supplier supplier;

	private Double price;

	@Column(name = "name_de")
	private String nameDe;
	
	@Column(name = "name_en")
	private String nameEn;

	@Column(name = "description_de")
	private String descriptionDe;
	
	@Column(name = "description_en")
	private String descriptionEn;
	
	...
}
Using Entities with Separate Language Columns

You can use these entity attributes in the same way as any other entity attributes.

When you persist a new Product entity, you call the setter methods of each localized name attribute with the translated version of the product name.

Product p = new Product();
p.setPrice(19.99D);
p.setNameDe("Hibernate Tips - Mehr als 70 Lösungen für typische Hibernateprobleme");
p.setNameEn("Hibernate Tips - More than 70 solution to common Hibernate problems");
p.setDescriptionDe("Wenn Du Hibernate in Deinen Projekten einsetzt, stellst Du schnell fest, dass ...");
p.setDescriptionEn("When you use Hibernate in your projects, you quickly recognize that you need to ...");
em.persist(p);

Hibernate then includes these columns in the SQL INSERT statement and stores all translations in the database. If you use my recommended logging configuration for development systems, you can see the executed SQL statements in the log file.

19:14:27,599 DEBUG SQL:92 - 
    select
        nextval ('hibernate_sequence')
19:14:27,735 DEBUG SQL:92 - 
    insert 
    into
        Product
        (description_de, description_en, name_de, name_en, price, supplier_id, version, id) 
    values
        (?, ?, ?, ?, ?, ?, ?, ?)

And when you fetch an entity from the database, you can call the getter methods for your preferred locale to retrieve the translated name and description. In the following example, I use the getNameEn and getDescriptionEn methods to get the English version of the product name and description.

Product p = em.createQuery("SELECT p FROM Product p WHERE id = 101", Product.class).getSingleResult();
log.info("Product: "+p.getNameEn());
log.info("Product Description: "+p.getDescriptionEn());

As you can see in the log messages, Hibernate uses a simple, efficient SQL statement to get the Product entity with the given id.

19:16:12,406 DEBUG SQL:92 - 
    select
        product0_.id as id1_0_0_,
        product0_.description_de as descript2_0_0_,
        product0_.description_en as descript3_0_0_,
        product0_.name_de as name_de4_0_0_,
        product0_.name_en as name_en5_0_0_,
        product0_.price as price6_0_0_,
        product0_.supplier_id as supplier8_0_0_,
        product0_.version as version7_0_0_ 
    from
        Product product0_ 
    where
        product0_.id=?
19:16:12,426  INFO UsabilityText:64 - Product: Hibernate Tips - More than 70 solutions to common Hibernate problems
19:16:12,427  INFO UsabilityText:65 - Product Description: When you use Hibernate in your projects, you quickly recognize that you need to ...
Pros & Cons of Entities with Separate Language Columns

As you have seen, adding a dedicated column for each translation to your table:

  • is very easy to implement in the table model,
  • is very easy to map to an entity and
  • enables you to fetch all translations with a simple query that doesn’t require any JOIN clauses.

But on the downside:

  • this mapping might require a lot of database columns if you need to translate multiple attributes into various languages,
  • fetching an entity loads translations that you might not use in your use case and
  • you need to update the database schema if you need to support a new language.

In my experience, the inflexibility of this approach is the biggest downside. If your application is successful, your users and sales team will request additional translations. The required schema update makes supporting a new language much harder than it should be. You not only need to implement and test that change, but you also need to update your database without interrupting your live system.

The next approach avoids these problems, and I, therefore, recommend it for most applications.



Already a member? Login here.

Different Tables and Entities for Translated and Non-Translated Fields

Instead of storing all the information in the same database table, you can also separate the translated and non-translated fields into 2 tables. That enables you to model a one-to-many association between the non-translated fields and the different localizations.

Here you can see a table model that applies this approach to the previously discussed example.

The LocalizedProduct table stores the different translations of the product name and description. As you can see in the diagram, that table contains a record for each localization of a product. So, if you want to store an English and a German name and description of your product, the LocalizedProduct table contains 2 records for that product. And if you’re going to support an additional language, you only need to add another record to the LocalizedProduct table instead of changing your table model.

Creating Your Entity Mappings

The entity model is almost identical to the table model. You map the non-translated columns of the Product table to the Product entity and the translated columns of the LocalizedProduct table to the LocalizedProduct entity. And between these 2 entity classes, you can model a managed many-to-one association.

Entity with Translated Fields – The LocalizedProduct entity

The following mapping of the LocalizedProduct entity consists of a few mandatory and an optional part. Let’s first talk about the mandatory mapping of the primary key and the association to the Product entity.

@Entity
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class LocalizedProduct {

	@EmbeddedId
	private LocalizedId localizedId;
	
	@ManyToOne
	@MapsId("id")
	@JoinColumn(name = "id")
	private Product product;
	
	private String name;
	
	private String description;

	...
}

The LocalizedProduct entity represents the to-many side of the association. The Product product attribute, therefore, owns the relationship definition. The @JoinColumn annotation tells Hibernate to use the id column of the LocalizedProduct table as the foreign key column. And the @MapsId annotation defines that the primary key value of the associated Product entity is part of the composite primary key of the LocalizedProduct entity. It gets mapped to the id attribute of the primary key class.

As I explain in great details in the Advanced Hibernate Online Training, you can map a composite primary key in various ways with JPA and Hibernate. In this example, I use an embedded id and an embeddable called LocalizedId.

As you can see in the following code snippet, the LocalizedId class is a basic Java class which implements the Serializable interface and is annotated with @Embeddable. And because you want to use it as an embedded id, you also need to make sure to implement the equals and hashCode methods.

@Embeddable
public class LocalizedId implements Serializable {

	private static final long serialVersionUID = 1089196571270403924L;

	private Long id;

	private String locale;

	public LocalizedId() {
	}

	public LocalizedId(String locale) {
		this.locale = locale;
	}

	// getter and setter methods ...

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((locale == null) ? 0 : locale.hashCode());
		result = prime * result
				+ ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		LocalizedId other = (LocalizedId) obj;
		if (locale == null) {
			if (other.locale != null)
				return false;
		} else if (!locale.equals(other.locale))
			return false;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}
}

OK, these were the necessary mapping parts of the LocalizedProduct entity. They map the composite primary key and the association to the Product entity.

If you want to take it one step further, you might also want to cache the LocalizedProduct entity. You can do that by activating the cache in your persistence.xml configuration and by annotating the LocalizedProduct entity with JPA’s @Cacheable or Hibernate’s @Cache annotation. As I explain in my Hibernate Performance Tuning Online Training, caching is a two-edged sword. It can provide substantial performance benefits but also introduce an overhead which can slow down your application. You need to make sure that you only change data that gets often read but only rarely changed. In most applications, that’s the case for the localized Strings. That makes them excellent candidates for caching.

Entity with Non-Translated Fields – The Product entity

After we mapped the LocalizedProduct table, which represents the different translations of the localized fields, it’s time to work on the mapping of the Product table.

Watch it on YouTube

Follow me on YouTube to not miss any new videos.

The only difference to the previous example is the mapping of the localized attributes. Instead of mapping an attribute for each translation, I’m using the localizations attribute. It maps the referencing side of the many-to-one association to the LocalizedProduct entity to a java.util.Map. This is one of the more advanced association mappings defined by the JPA specification, and I explained in great details in How to map an association as a java.util.Map.

In this example, I use the locale attribute of the LocalizedProduct entity as the key and the LocalizedProduct entity as the value of the Map. The locale is mapped by the LocalizedId embeddable, and I need to specify the path localizedId.locale in the @MapKey annotation.

The mapping to a java.util.Map makes accessing a specific translation in your business code more comfortable. And it doesn’t affect how Hibernate fetches the association from the database. In your JPQL or Criteria Queries, you can use this association in the same way as any other managed relationship.

@Entity
public class Product {

	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE)
	private Long id;

	@Version
	private int version;

	@ManyToOne(fetch = FetchType.LAZY)
	private Supplier supplier;

	private Double price;

	@OneToMany(mappedBy = "product", cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, orphanRemoval = true)
	@MapKey(name = "localizedId.locale")
	@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
	private Map<String, LocalizedProduct> localizations = new HashMap<>();

	...
	
	public String getName(String locale) {
		return localizations.get(locale).getName();
	}

	public String getDescription(String locale) {
		return localizations.get(locale).getDescription();
	}
}

If you want to make your entity model more comfortable to use, you could activate orphanRemoval for the association. That is a general best practice for one-to-many associations that model a parent-child relationship in which the child can’t exist without its parent. It tells your JPA implementation, e.g., Hibernate, to delete the child entity as soon as its association to the parent entity gets removed. I use it in this example to remove a LocalizedProduct entity as soon as it’s no longer associated with a Product entity.

Watch it on YouTube

Follow me on YouTube to not miss any new videos.

Another thing you could do to improve the usability of your entities is to provide getter methods that return the product name and description for a given locale. If you implement additional getter methods to return a localized name and description, you need to keep in mind that they are accessing a lazily fetched one-to-many association. That triggers an additional SQL statement if the association isn’t already fetched from the database. You can avoid that by using a JOIN FETCH clause or an entity graph to initialize the association while loading your Product entity.

And if you activated the 2nd level cache on the LocalizedProduct entity, you should also annotate the localizations attribute with Hibernate’s @Cache annotation. That tells Hibernate to cache the association between these 2 entities. If you miss this annotation, Hibernate will execute a query to retrieve the associated LocalizedProduct entities even though they might be already in the cache. That is another example of how complex caching with Hibernate can be. It’s also one of the reasons why the Hibernate Performance Tuning Online Training includes a very detailed lecture about it.

Using Different Entities for Translated and Non-Translated Fields

Using this mapping is a little harder than the previous one. The translations are now mapped by an associated entity. It gets a little bit easier if you activate CascadeType.PERSIST, so that you can persist your Product entity and Hibernate automatically cascades this operation to all associated LocalizedProduct entities.

And because I modeled a bidirectional association between the Product and the LocalizedProduct entity, I always need to make sure to update both ends of the relationship.

Product p = new Product();
p.setPrice(19.99D);

LocalizedProduct lpDe = new LocalizedProduct();
lpDe.setId(new LocalizedId("de"));
lpDe.setProduct(p);
lpDe.setName("Hibernate Tips - Mehr als 70 Lösungen für typische Hibernateprobleme");
p.getLocalizations().put("de", lpDe);

LocalizedProduct lpEn = new LocalizedProduct();
lpEn.setId(new..
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The post Hibernate Tips: What’s the Difference Between @Column(length=50) and @Size(max=50) appeared first on Thoughts on Java.

Hibernate Tips is a series of posts in which I describe a quick and easy solution for common Hibernate questions. If you have a question for a future Hibernate Tip, please post a comment below.

Question:

Some of our entity attributes are annotated with @Column(length=50), others with @Size(max=50) and others with both of these annotations.

What’s the difference between the two annotations? Does it make any difference which one we use?

Or do we need both of them?

Solution:

Even though it might seem like both annotations do the same, there are a few crucial differences:

  1. The @Colum annotation is part of the JPA specification, while the @Size annotation belongs to the BeanValidation specification. So, if you want to use the @Size annotation, you need to add an additional dependency to your project.
  2. The length attribute of the @Column annotation specifies the size of the database column and gets only used when your persistence provider generates the database schema. But the @Size annotation tells your BeanValidation implementation to perform a proper validation at runtime.
  3. If you use the @Column annotation and let your persistence provider generate the database schema, your database will reject any values that are more than 50 characters long. The @Size annotation gets evaluated within your Java application before the entity gets persisted or updated.

Let’s take a look at an example.

Using the @Column(size=50) Annotation

If you annotate the title attribute of a Book entity with @Column(size=50), the only thing Hibernate will do it to generate a CREATE TABLE statement that limits the size of the column to 50 characters.

@Entity
public class Book {

	@Id
	@GeneratedValue
	private Long id;
	
	@Column(length=50)
	private String title;

	...
}
create table Book (
   id int8 not null,
   title varchar(50),
   version int4 not null,
   primary key (id)
)

If you use that CREATE TABLE statement to create your database table, the database will return an error if you try to store more than 50 characters in a title field. But Hibernate will not perform any validation within your application.

Using the @Size(max=50) Annotation

Let’s replace use the @Size(max=50) annotation instead of the previously used @Column annotation.

@Entity
public class Book {

	@Id
	@GeneratedValue
	private Long id;
	
	@Size(max=50)
	private String title;

	...
}

As I explained in great details in a previous article, the JPA and the BeanValidation specification integrate very well. In this case, that provides 2 main benefits:

  1. If you configure Hibernate to generate the database tables, it will limit the size of the database column based on the maximum size defined by the @Size annotation. As a result, you get the same CREATE TABLE statement as in the previous example.
  2. Hibernate will automatically trigger your BeanValidation implementation before it persists or updates an entity. That gives you another layer of protection that doesn’t rely on the execution of a specific, generated DDL statement.
Which One Should You Use? @Column(size=50) or @Size(max=50)?

As you’ve seen in the previous paragraphs, Hibernate generates the same CREATE TABLE statements for both annotations. But for the @Size annotation, Hibernate automatically triggers a validation before it inserts or updates the entity.

That makes the @Size annotation the safer and more powerful approach to validate the size of the value of an entity attribute.

Get this Hibernate Tip as a printable PDF!Join the free Thoughts on Java Library to get access to lots of member-only content, like a printable PDF for this post, lots of cheat sheets and 2 ebooks about Hibernate.

Already a member? Login here.
Learn more:

To learn more about BeanValidation and its integration with JPA, please take a look at the following articles:

 
Hibernate Tips Book
Get more recipes like this one in my new book Hibernate Tips: More than 70 solutions to common Hibernate problems.

It gives you more than 70 ready-to-use recipes for topics like basic and advanced mappings, logging, Java 8 support, caching and statically and dynamically defined queries.

Get it now as a paperback, ebook or PDF.

The post Hibernate Tips: What’s the Difference Between @Column(length=50) and @Size(max=50) appeared first on Thoughts on Java.

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

The post Hibernate Tips: How to perform different validations for persist and update appeared first on Thoughts on Java.

Hibernate Tips is a series of posts in which I describe a quick and easy solution for common Hibernate questions. If you have a question for a future Hibernate Tip, please post a comment below.

Question:

I’m using BeanValidation to validate my entities automatically before they get persisted and updated. I defined different ValidationGroups for INSERT and UPDATE operations.

Can I tell Hibernate which ValidationGroups it shall use before persisting or updating an entity?

Solution:

Yes, the JPA specification defines a set of configuration parameters to configure which ValidationGroups shall be validated before performing an insert, update, and remove operations.

Let’s take a look at an example.

Defining the Validation

The JPA and BeanValidation specifications integrate very easily. You can annotate your entity attributes with BeanValidation annotations, which specify the validation you want to perform. The validation will then get triggered automatically before each lifecycle state change.

In the following code snippet, I use the @Min and the @Max annotations to ensure that the numPages attribute contains a value between 100 and 1000.

@Entity
public class Book {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;

	private String title;
	
	@Min(value = 100, groups = PublishedBook.class)
	@Max(1000)
	private int numPages;

	...
}

The @Min validation references the PublishedBook interface as a ValidationGroup. This validation is now part of the PublishedBook ValidationGroup and no longer part of the default group.

public interface PublishedBook {}
Configuring Different ValidationGroups for Persist and Update

In the next step, you can set which ValidationGroups shall be validated before an entity gets persisted, updated, or removed. By default, your JPA implementation uses the default group before persisting and updating an entity and doesn’t perform any validation before removing it.

You can change that in your persistence.xml file using the following parameters:

  • javax.persistence.validation.group.pre-persist
  • javax.persistence.validation.group.pre-update
  • javax.persistence.validation.group.pre-remove

In this example, I use the javax.persistence.validation.group.pre-update to tell Hibernate to validate the org.thoughts.on.java.validation.PublishedBook ValidationGroup before an entity gets updated.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.1"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
	<persistence-unit name="my-persistence-unit">
		<description>Hibernate Tips</description>
		<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
		<exclude-unlisted-classes>false</exclude-unlisted-classes>

		<properties>
			<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />

			<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
			<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/recipes" />
			<property name="javax.persistence.jdbc.user" value="postgres" />
			<property name="javax.persistence.jdbc.password" value="postgres" />

			<property name="javax.persistence.validation.group.pre-update" value="org.thoughts.on.java.validation.PublishedBook" />
		</properties>
	</persistence-unit>
</persistence>

I don’t change the default groups for the pre-persist and pre-remove validation. So, before an entity gets persisted, Hibernate will trigger the validation of the default group and will not trigger any validation before the entity gets removed.

Get this Hibernate Tip as a printable PDF!Join the free Thoughts on Java Library to get access to lots of member-only content, like a printable PDF for this post, lots of cheat sheets and 2 ebooks about Hibernate.

Already a member? Login here.
Learn more:

To learn more about BeanValidation and its integration with JPA, please take a look at the following articles:

 
Hibernate Tips Book
Get more recipes like this one in my new book Hibernate Tips: More than 70 solutions to common Hibernate problems.

It gives you more than 70 ready-to-use recipes for topics like basic and advanced mappings, logging, Java 8 support, caching and statically and dynamically defined queries.

Get it now as a paperback, ebook or PDF.

The post Hibernate Tips: How to perform different validations for persist and update appeared first on Thoughts on Java.

Read for later

Articles marked as Favorite are saved for later viewing.
close
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Separate tags by commas
To access this feature, please upgrade your account.
Start your free month
Free Preview