Showing posts with label JPA. Show all posts
Showing posts with label JPA. Show all posts

Saturday, August 3, 2019

JDBC - Emulating a sequence

Probably each of us encountered this problem at least once in the programmer's life - how to emulate a database sequence? Below you may find my variation of this problem's solution.

Suppose that we have an interface defining the desired API for returning a sequence of integer numbers:
public interface Sequences {

    int nextValue(String sequenceName) throws SQLException;

}
and the implementation of this API in the following form:
class SequencesService implements Sequences {

    private static final String SQL_QUERY =
        "SELECT SEQ_NAME, SEQ_VALUE FROM SEQUENCE WHERE SEQ_NAME = ? FOR UPDATE";

    private final DataSource dataSource;

    SequencesService(final DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public int nextValue(final String sequenceName) throws SQLException {
        final long threadId = Thread.currentThread().getId();

        try (final Connection connection = dataSource.getConnection()) {
            connection.setAutoCommit(false);
            try (final PreparedStatement statement =
                     connection.prepareStatement(
                         SQL_QUERY, TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE)) {
                statement.setString(1, sequenceName);
                try (final ResultSet resultSet = statement.executeQuery()) {
                    System.out.println(
                        String.format("[%d] - select for update", threadId));
                    int nextValue = 1;
                    if (resultSet.next()) {
                        nextValue = 1 + resultSet.getInt(2);
                        resultSet.updateInt(2, nextValue);
                        resultSet.updateRow();
                    } else {
                        resultSet.moveToInsertRow();
                        resultSet.updateString(1, sequenceName);
                        resultSet.updateInt(2, nextValue);
                        resultSet.insertRow();
                    }
                    System.out.println(
                        String.format("[%d] - next val: %d", threadId, nextValue));
                    return nextValue;
                }
            } finally {
                System.out.println(String.format("[%d] - commit", threadId));
                connection.commit();
            }
        }
    }

}
You have to forgive me two things :) - the println usage, which I added for generating some visual feedback ;) and a lack of detailed explanation how this solution works ;) I'll just mention that the clue is the way prepared statement is created, and the result set handling: updateRow / moveToInsertRow / insertRow usage ;) (see the links at the bottom of this post for the details).

I wrote simple test case to observe and verify this code, something like:
@Autowired
private Sequences sequences;

private Callable<Integer> callable() {
    return () -> {
        System.out.println(String.format("[%d] - starting", Thread.currentThread().getId()));
        return sequences.nextValue("My Sequence");
    };
}

@Test
public void test() throws Exception {
    final ExecutorService executor = Executors.newFixedThreadPool(3);
    final CompletionService<Integer> completion = new ExecutorCompletionService<>(executor);

    for (int i = 0; i < 3; i++) {
        completion.submit(callable());
    }
    
    for (int completed = 1; completed <= 3; completed++) {
        final Future<Integer> result = completion.take();
        System.out.println(String.format("Result %d - %d", completed, result.get()));
        assertEquals(Integer.valueOf(completed), result.get());
    }
}
When run, the above code, the output will be something like this (threads' IDs in the brackets):
[16] - starting
[18] - starting
[17] - starting
[17] - select for update
[17] - next val: 1
[17] - commit
[18] - select for update
Result 1 - 1
[18] - next val: 2
[18] - commit
[16] - select for update
[16] - next val: 3
[16] - commit
Result 2 - 2
Result 3 - 3

This code is just for demonstration purposes :) - if you want to do something similar in your project, it's probable that you will rather use for ex. Spring Framework's @Transactional annotation, instead of manual transactions handling, or even JPA delegating this work to JDBC. For example in Hibernate you may do it somehow like this:
import org.hibernate.Session;
...

entityManager.unwrap(Session.class)
                      .doReturningWork(connection -> { ... code derived from my example ... });

Few links for the dessert:
... and I almost forgot ;) - GitHub repository holding all my code expriments for this post


Follow-ups:

 

This article has been republished on DZone's Database Zone (08/21/2019).

Sunday, May 4, 2014

@OneToOne with shared primary key, revisited :)

Long time ago, I wrote a post @OneToOne with shared primary key. Today I would like to return to this problem, with solution based on @MapsId annotation introduced in JPA 2.0

Again we have two entities: Primus and Secundus. Both entities have primary key using Long Java type. They are related 1-1, and Secundus should use the same primary key as Primus.
3 Years after my initial post they will look slightly different ;)
@Entity
@Table(name = "PRIMUS")
public class Primus {

    public static Primus newInstance() {
        Primus primus = new Primus();
        primus.secundus = new Secundus(primus);
        return primus;
    }

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

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "primus")
    private Secundus secundus;

    Primus() {
        super();
    }
    ...

}
Not much changed here, ;). Really important changes were made on the Secundus:
@Entity
@Table(name = "SECUNDUS")
public class Secundus {

    @Id
    private Long id;

    @JoinColumn(name = "ID")
    @OneToOne
    @MapsId
    private Primus primus;

    Secundus() {
        super();
    }

    public Secundus(Primus primus) {
        this();
        this.primus = primus;
    }
As you see, the @PrimaryKeyJoinColumn annotation is replaced with two annotations: @MapsId, which defines that Secundus identifier will be determined by Primus identifier, and @JoinColumn, specifying which column in SECUNDUS table will be used for joining. 

Nothing more is needed :) - JPA Provider should automatically ask Primus for its identifier, when persisting Secundus, as long as you take care of correct entities correlation, like in newInstance method of Primus.

Long live JPA 2.0+ ;) :) 

Saturday, May 11, 2013

JPA - Querydsl Projections

In my last post: JPA - Basic Projections - I've mentioned about two basic possibilities of building JPA Projections. This post brings you more examples, this time based on Querydsl framework. Note, that I'm referring Querydsl version 3.1.1 here.

Reinvented constructor expressions


Take a look at the following code:

The above Querydsl construction means: create new JPQL query [1] [2], using employee as the data source, order the data using employee name [3], and return the list of EmployeeNameProjection, built using the 2-arg constructor called with employee ID and name [4].  This is very similar to the constructor expressions example from my previous post (JPA - Basic Projections), and leads to the following SQL query:

select EMPLOYEE_ID, EMPLOYEE_NAME from EMPLOYEE order by EMPLOYEE_NAME asc

As you see above, the main advantage comparing to the JPA constructor expressions is using Java class, instead of its name hard-coded in JPQL query.

Even more reinvented constructor expressions


Querydsl documentation [4] describes another way of using constructor expressions, requiring @QueryProjection annotation and Query Type [1] usage for projection, see example below. Let's start with the projection class modification - note that I added @QueryProjection annotation on the class constructor.

Now we may use modified projection class (and corresponding Query Type [1] ) in following way:

Which leads to SQL query:

select EMPLOYEE_ID, EMPLOYEE_NAME from EMPLOYEE order by EMPLOYEE_NAME asc

In fact, when you take a closer look at the Query Type [1] generated for EmployeeNameProjection (QEmployeeNameProjection), you will see it is some kind of "shortcut" for creating constructor expression the way described in first section of this post.

Mapping projection


Querydsl provides another way of building projections, using factories based on MappingProjection.


The above class is a simple factory creating EmployeeNameProjection instances using employee ID and name. Note that the factory constructor defines which employee properties will be used for building the projection, and map method defines how the instances will be created.

Below you may find an example of using the factory:

As you see, the one and only difference here, comparing to constructor expression examples, is the list method call.

Above example leads again to the very simple SQL query:

select EMPLOYEE_ID, EMPLOYEE_NAME from EMPLOYEE order by EMPLOYEE_NAME asc

Building projections this way is much more powerful, and doesn't require existence of n-arg projection constructor.

QBean based projection (JavaBeans strike again)


There is at least one more possibility of creating projection with Querydsl - QBean based - in this case we build the result list using:

... .list(Projections.bean(EmployeeNameProjection.class, employee.employeeId, employee.name))

This way requires EmployeeNameProjection class to follow JavaBean conventions, which is not always desired in application. Use it if you want, but you have been warned ;)

Few links for the dessert

  1. Using Query Types
  2. Querying
  3. Ordering
  4. Constructor projections


Follow-ups:


This article has been republished on Java Code Geeks (05/14/2013), and on Dzone's Javalobby (05/15/2013).

Saturday, May 4, 2013

JPA - Basic Projections

In my last post: JPA - Should I become a laziness extremist? - I mentioned about the possibilities of improving JPA usage - one of them is using Projections.

Projection is a subset of entities' properties. It can be represented as dedicated class (or classes), and mapped either as the database view based entity, or using constructor expressions [1][2]. The clue of this solution is having very limited entities tree (or no tree at all, as in my example) comparing to original entity (Employee). We need to display employee name, thus we build the projection having employee name and ID only. As you will see below, using projections leads to single SQL query, instead of bunch of SQL queries (see example in JPA - Should I become a laziness extremist?)

Database view based entities


What we need in this case is simple JPA entity, mapped to any database view (or table if desired properties are in one table).

Now we can run JPQL query:

select employee from ViewBasedEmployeeNameProjection employee order by employee.name

which in turn will execute one single SQL query:

select EMPLOYEE_ID, EMPLOYEE_NAME from EMPLOYEE order by EMPLOYEE_NAME

Using this method you have to remember that you can choose anything for the projection ID, as long as it uniquely identifies each projection. 

Constructor expressions


What we need in this case is a class representing the projection with constructor having parameters corresponding to all properties in the projection.
 
Now we can run JPQL query:

select new com.blogspot.vardlokkur.domain.EmployeeNameProjection(employee.employeeId, employee.name) from Employee employee order by employee.name

which in turn will execute one single SQL query:

select EMPLOYEE_ID, EMPLOYEE_NAME from EMPLOYEE order by EMPLOYEE_NAME

This method has some disadvantages:
  • adding new projection properties increases number of constructor parameters
  • projection class name is included in JPQL query, which may lead to refactoring problems
Second disadvantage can be solved with Querydsl, which also gives you few more possibilities of building projections, but that will be subject of my next post :) 

To be continued ...

Few links for the dessert

  1. JPQL Constructor Expressions
  2. Result Classes (Constructor Expressions)


Follow-ups:


This article has been republished on Dzone's Javalobby (05/06/2013).

Saturday, April 27, 2013

JPA - Should I become a laziness extremist?

When you spoke with the Developers about mapping objects to relational databases, they very often complain about poor JPA performance, unpredictable behavior of JPA Providers, etc. Usually at some point of the conversation you will hear: "Let's drop this technology at all, we've seen something much better on the conference last month. We will use it in our projects instead of JPA and develop them happily ever after." - Sounds familiar? :)

It's nothing wrong in learning new technologies, in fact you should do it constantly, to improve your skills and knowledge, but when you have problems with one of them, will you choose an easy path to another technology, or ask yourself: "Am I using it in a right way?"

Let's look at the JPA usage example. Suppose that we have simple database, mapped to the entities:


and we have to display all employee names, regardless their employer (and department). Nothing easier ;) - simple JPQL query will do that:

select employee from Employee employee order by employee.name

Many developers finish at this point, and celebrate with Friends another successful JPQL query in their life ;), but some of us have this strange feeling, that something creepy is lurking beneath the shiny surface. SQL queries produced by the JPA provider (ex. Hibernate) will reveal the truth: 
select [...]  from EMPLOYEE employee0_ order by employee0_.EMPLOYEE_NAME

Nothing special, so far :), but here comes the naked truth:
select [...] from DEPARTMENT department0_ left outer join EMPLOYER employer1_ on department0_.EMPLOYER_ID=employer1_.EMPLOYER_ID where department0_.DEPARTMENT_ID=?
select [...] from EMPLOYER employer0_ where employer0_.EMPLOYER_ID=?
select [...] from DEPARTMENT department0_ left outer join EMPLOYER employer1_ on department0_.EMPLOYER_ID=employer1_.EMPLOYER_ID where department0_.DEPARTMENT_ID=?
select [...] from DEPARTMENT department0_ left outer join EMPLOYER employer1_ on department0_.EMPLOYER_ID=employer1_.EMPLOYER_ID where department0_.DEPARTMENT_ID=?
select [...] from DEPARTMENT department0_ left outer join EMPLOYER employer1_ on department0_.EMPLOYER_ID=employer1_.EMPLOYER_ID where department0_.DEPARTMENT_ID=?
What the heck?! What are these queries for?! - Well the reason lies in default fetch attribute values for @ManyToOne annotations, which is EAGER. My database holds 2 Employers, one of them has 4 Departments, while second one hasn't any. When the Employee is loaded, JPA provider loads by default all EAGER associations (in our case both Department, and Employer), thus we have the additional queries. As you see above the JPA provider is clever enough to load both Employer and Department at once, when it is possible.

You've just found magical JPQL query fetching all the database content at once :). Does this situation remind you something in the past? ;)

What can we do about it? - My Friend, all you need is a laziness :) - Don't use EAGER unless it is REALLY needed (and remember that @ManyToOne and @OneToOne annotations use it by default).

You may call me a lunatic, or laziness extremist at this point :) and ask: Have you ever encountered LazyInitializationException, Bro!? Have you heard of all the mess with lazy loading problems!? Performance degradation, etc. ... Of course I did :), but don't you think that if we are getting in such troubles with JPA, maybe we use it in a wrong way?!

What we do usually in Web Applications is presenting or editing some data on UI, and usually it is only small subset of specific entities' properties. Doing it requires fetching the entities tree from the database - without batting an eye, we ask Entity Manager: give me all Employees, sorted by name, with all related entities, and then complain on degraded performance!

We don't care what we fetch from the database, because Entity Manager will do the donkey work for us. We get LazyInitializationException, so what! We will use Open Entity Manager in View pattern, and silence this stupid exception!

Give a me a break! Don't you think it's a dead end? :) - It's about time to change something :) There are sophisticated methods which you can use in your projects, like CQRS for example, along with possibilities already existing in JPA, which can help you change the bad manners described by me in this post.

To be continued ...

Few links for the dessert:




Follow-ups:


This article has been republished on Java Code Geeks (05/01/2013), and on Dzone's Javalobby (05/01/2013).

Saturday, April 6, 2013

JPA - Hibernate - Type mapping on package level

When we are finally mature enough to use some custom types mapping in JPA, we usually stuck with some provider specific solution, because JPA itself doesn't define any mechanism for doing it. Let me show you an example of custom type mapping definition for one of the JPA providers - Hibernate.

Suppose that we use Joda Money in our project, and have an entity with property having type Money. There are already pretty nice type mapping implementations for Money, provided by Jadira - User Types project. All we have to do is just let Hibernate know that we want to use specific type mapping.

When you look at the Hibernate Docs, Section 5.1.4.1.1: Type, you'll see few possibilities, starting from the simplest - using @Type annotation on each property having Money type. This choice can be good if you have only one, or very few, properties of this type in your domain mapping. It is very probable that sooner or later, when your project will grow enough, there will be more and more of them, and you end up with many similar lines defining the same type mapping.

If you aren't a big fan of repeating yourself, or you don't trust in refactorings made by your apprentices ;), you should consider another way, using @TypeDefs and @TypeDef annotations.

As you may read in Hibernate documentation: "These annotations can be placed at the class or package level." - Let's focus on the second option - package level.

We will place these annotations in package-info.java for our domain entities holding package (see: Java Language Specification - 7.4.1. Named Packages). It will look like this:

Now, when you map the property using Money type, you can do it without additional type mapping specification, just like this:

One technical note, before you become happy Money mapping user ;) - Because PersistentMoneyAmount uses single column (holding amount) for Money mapping, it requires defining of currency which will be used along with the amount. The default currency can be defined as Persistence Unit property: jadira.usertype.currencyCode

PS. Don't treat the above Money example as the guideline of Joda Money mapping :), there are probably better ways of doing it, see Jadira User Types blog.

Few links for the dessert:





Follow-ups:


This article has been republished on Java Code Geeks (04/09/2013), and on Dzone's Javalobby (04/15/2013).

Monday, August 13, 2012

JPQL - pagination on Oracle Database with Hibernate

In your daily work, you rely on many different libraries, trusting they will serve you well, being perfect piece of code ... do you? ... really?! Then it's time to realize that you are perfectly wrong :) Increasing complexity of code leads to new possibilities of making errors :) Many of them are lurking in the libraries used by you, even if they are used for years by thousands of developers.

Let's find some example. Suppose that we are using JPA, and have an entity named Employee, which contains at least two properties: name and id. Suppose that we want to display all employees ordered by name, and paginated. To fetch them from the underlying database we will need JPQL query like this:

select e from Employee e order by e.name

We will paginate it using setFirstResult and setMaxResults methods of javax.persistence.Query interface. Now we need a JPA provider and database to make it work, let's choose Hibernate, and Oracle (10+).

At first try everything works perfectly :) - but let's assume that we have employees sharing the same name, for ex. 20 of them having name 'Smith' (identifiers between 1 and 20), 10 having name 'Donovan' (identifiers between 21 and 30) and 10 having name 'Johnson' (identifiers between 31 and 40) - total 40 employees. Let's try to display 5 employees on single page, and see what will happen:

Page 1 - employees having ID: 21, 25, 24, 23, 22 - all having name 'Donovan' - good :)
Page 2 - employees having ID: 26, 25, 24, 23, 22 - all having name 'Donovan' - but 4 of them were already displayed on first page (!)
Page 3 - employees having ID: 31, 35, 34, 33, 32 - here comes the 'Johnson' name - good again :)
Page 4 - employees having ID: 36, 35, 34, 33, 32 - 'Johnson' again, and again 4 of them were already displayed on third page (!!)
Page 5 - employees having ID: 1, 17, 18, 19, 20 - here comes the 'Smith' name - good again :)
and finally the real surprise - Pages 6, 7 and 8 contains same employees - having ID: 16, 17, 18, 19, 20

Don't you think something is wrong here ?! ;) Well, the reason of this strange error is visible when you check SQL queries generated by Hibernate:

for the first 5 rows and:


for the rows 6 - 10 (and similar for next pages).

What the heck?! - you may say - These beautiful SQL queries are suggested for pagination on Oracle's website - see Tom Kyte's article: On ROWNUM and Limiting Results - sure :) - but the one who implemented it in Hibernate didn't read this article too deeply, skipping this important part:

One important thing about using this pagination query is that the ORDER BY statement should order by something unique. If what you are ordering by is not unique, you should add something to the end of the ORDER BY to make it so.

As you see, correct JPQL query leads to invalid SQL query for Hibernate / Oracle combination, and the error shows himself only for some combinations of data in the database - it is very difficult to spot because of that.

Do you still think that libraries used by you are perfect? :)

PS: This article has been created thanks to one of my Colleagues - Joanna Głowińska - her awesome work on SQL queries in some of our projects lead me to the above thoughts.



Follow-ups:


This article has been republished on Dzone's Javalobby (08/27/2012), with interesting comments from Gavin King, Karl Peterbauer, Andrew Thorburn, and Tomasz Wermiński.

Saturday, June 25, 2011

JPQL and joins

Have you ever asked yourself if JPQL (Java Persistence Query Language) queries written by you REALLY do what you want, or you just prepare them, commit to the code repository, and forget about them with a little help from pizza and beer ;) ...

Let's take a look at very simple example, we have Person entity:
@Entity
@Table(name = "PERSONS")
public class Person implements Serializable {

    private String firstName;
    private Long id;
    private Person spouse;

    @Column(name = "FIRST_NAME")
    public String getFirstName() {
        return firstName;
    }

    @Id
    public Long getId() {
        return id;
    }

    @OneToOne(optional = true)
    @JoinColumn(name = "SPOUSE_ID")
    public Person getSpouse() {
        return spouse;
    }
...
}
and we want to fetch all persons matching some criteria.

Suppose that PERSONS table has following entries:

ID SPOUSE_ID FIRST_NAME
1 NULL John
2 NULL Mary
3 4 Adam
4 3 Eva

We will build and execute very simple JPQL query:
Query query = entityManager.createQuery(
    "select person from Person person where person.spouse.firstName = 'Eva' or 1 = 1");
Assert.assertEquals("Result list is too small,", 4, query.getResultList().size());
Our JPQL query should return all persons from the database, because of the "1 = 1" expression in where clause. Let's try how it works with different JPA Providers.

EclipseLink (2.2.0) converts our JPQL query into following SQL query:
SELECT t1.ID, t1.FIRST_NAME, t1.SPOUSE_ID 
FROM PERSONS t0, PERSONS t1 
WHERE (((t0.FIRST_NAME = 'Eva') OR (1 = 1)) AND (t0.ID = t1.SPOUSE_ID))
which leads to assertion error:
java.lang.AssertionError: Result list too small, expected:<4> but was:<2>small 
Hibernate (3.4.0 GA) uses following SQL:
select person0_.id as id1_, person0_.FIRST_NAME as FIRST2_1_, person0_.SPOUSE_ID as SPOUSE3_1_ 
from PERSONS person0_, PERSONS person1_ 
where person0_.SPOUSE_ID=person1_.id and (person1_.FIRST_NAME='Eva' or 1=1)
which leads to the same assertion failure.

Only OpenJPA (2.1.0) uses SQL expected by us:
SELECT t0.id, t0.FIRST_NAME, t1.id, t1.FIRST_NAME 
FROM PERSONS t0 
    LEFT OUTER JOIN PERSONS t1 ON t0.SPOUSE_ID = t1.id 
WHERE (t1.FIRST_NAME = 'Eva' OR 1 = 1)
How can we force EclipseLink and Hibernate to use left join? We have to modify JPQL query in following way:
select person 
from Person person 
    left join person.spouse spouse 
where spouse.firstName = 'Eva' or 1 = 1
As you see I've added explicit left join and changed first where clause expression to use this join.
Now JPQL query returns all 4 records regardless of used JPA Provider.

Sunday, May 29, 2011

EclipseLink - JPA Queries optimization - @JoinFetch, @BatchFetch and Query Hints

@JoinFetch is very helpful when you try to minimize the number of SQL queries standing behind JPA query. I wrote about it in one of my previous posts: JPA Demystified (episode 1) - @OneToMany and @ManyToOne mappings, but it brings also some threats (see JPA - insert instead of update). This post will describe one of them, related to limiting number of results returned by JPA query.

Let's use same entities as in JPA Demystified (episode 1) - @OneToMany and @ManyToOne mappings. We have Employer:
@Entity
@Table(name = "EMPLOYERS")
public class Employer implements Serializable {
    ...
    private List<Employee> employees = new ArrayList<Employee>();
    ...
    @OneToMany(mappedBy = "employer", cascade = CascadeType.ALL)
    @JoinFetch(JoinFetchType.OUTER)
    public List getEmployees() {
        return employees; 
    }
    ...
}
and Employee:
@Entity
@Table(name = "EMPLOYEES")
public class Employee implements Serializable {
    ...
    private Employer employer;
    ...
    @ManyToOne(optional = false)
    @JoinColumn(name = "EMPLOYER_ID")
    @JoinFetch(JoinFetchType.INNER)
    public Employer getEmployer() {
        return employer;
    }
    ...
}
As you see, nothing special, well maybe except the @JoinFetch annotation on employees property of Employer.

We will use the query fetching only few employers from the DB:
    ...
    @PersistenceContext
    private EntityManager entityManager;
    ...
    TypedQuery<Employer> query = entityManager.createQuery("select e from Employer e", Employer.class);
    query.setMaxResults(5);    
    List<Employer> employers = query.getResultList();
    Assert.assertEquals(5, employers.size());    
    employers.get(0).getEmployees().get(0);
    ...
Suppose that we have 8 employers in the database, each of them have 5 employees. When you run the above code you'll receive: java.lang.AssertionError: expected:<5> but was:<1>. Why?

The truth is hidden in the dark ;) - JPA query will produce following SQL (target DB is MySQL):
SELECT 
    t1.ID AS a1, t1.BUSINESS_NAME AS a2, 
    t0.ID AS a3, t0.FIRST_NAME AS a4, t0.EMPLOYER_ID AS a5 
FROM EMPLOYERS t1 
    LEFT OUTER JOIN EMPLOYEES t0 ON (t0.EMPLOYER_ID = t1.ID) 
LIMIT 0, 5
As you see, @JoinFetch annotation forced EclipseLink to fetch employers and employees in one SQL query, but limiting number of records is performed the same way as for fetching the employers only, and therefore the result set will be limited to: Employer 1 - Employee 1, Employer 1 - Employee 2, ... , Employer 1 - Employee 5. This result set will be in turn converted by EclipseLink to one employer with 5 employees attached, and the result of the query will be 1 Employer, instead of expected 5.

Good Lord! Is there any hope for the people looking for some queries optimization?! - Of course my dear Watson :) - we can always replace one EclipseLink specific annotation (@JoinFetch) with another (@BatchFetch) or the JPA Query hints ...

Let's focus on the @BatchFetch annotation first. We will modify the Employer entity in the following way:
...
    @OneToMany(mappedBy = "employer", cascade = CascadeType.ALL)
    @BatchFetch(BatchFetchType.JOIN)
    public List<Employee> getEmployees() {
        return employees;
    }
    ...
The @BatchFetch annotation tells EclipseLink to fetch all related objects in one query, so instead of one query (when using @JoinFetch) we will have two (one for fetching employers, and second one for fetching the related employees). As you probably suppose, our code will work correctly this time, at least on the Java level ;) - because when we check the generated SQL, we will see:
SELECT ID AS a1, BUSINESS_NAME AS a2 FROM EMPLOYERS LIMIT 0, 5

SELECT t1.ID, t1.FIRST_NAME, t1.EMPLOYER_ID, t0.ID, t0.BUSINESS_NAME 
FROM EMPLOYERS t0, EMPLOYERS t2, EMPLOYEES t1 
WHERE ((t1.EMPLOYER_ID = t2.ID) AND (t0.ID = t1.EMPLOYER_ID))
As you see it's not the optimal choice - we fetch only 5 employers and ALL employees, from which only those who are bound to one of our 5 employers are used. Waste of DB and network resources :(

When you try EXISTS type of @BatchFetch the queries looks like this:
SELECT ID AS a1, BUSINESS_NAME AS a2 FROM EMPLOYERS LIMIT 0, 5

SELECT t1.ID, t1.FIRST_NAME, t1.EMPLOYER_ID, t0.ID, t0.BUSINESS_NAME 
FROM EMPLOYERS t0, EMPLOYEES t1 
WHERE (
    EXISTS (
        SELECT t2.ID FROM EMPLOYERS t2 
        WHERE (t1.EMPLOYER_ID = t2.ID)
    ) AND (t0.ID = t1.EMPLOYER_ID)
)
Still not the optimal choice, same as the above we fetch 5 employers and ALL employees :(

Only the IN type produces the SQL, which can be useful for us:
SELECT ID AS a1, BUSINESS_NAME AS a2 FROM EMPLOYERS LIMIT 0, 5

SELECT t1.ID, t1.FIRST_NAME, t1.EMPLOYER_ID, t0.ID, t0.BUSINESS_NAME 
FROM EMPLOYERS t0, EMPLOYEES t1 
WHERE ((t1.EMPLOYER_ID IN (1, 2, 3, 4, 5)) AND (t0.ID = t1.EMPLOYER_ID))
As you see @BatchFetch annotation give us possibility to fetch exactly 5 employers, and limit the number of required SQL queries to 2. There is one potential problem with IN type usage, which is related to limit in number of expressions for SQL IN clause for some databases (ex. Oracle - max 1000 expressions).

If you prefer, for some reason, to control the employees fetching on per-query basis, you have to switch from the annotations usage to Query Hints, for ex.
query.setHint(QueryHints.BATCH, "e.employees");
query.setHint(QueryHints.BATCH_TYPE, BatchFetchType.IN);
Few lectures for the dessert:

Wednesday, May 4, 2011

@OneToOne with shared primary key

A Friend of mine asked me lately how would I define @OneToOne mapping in JPA with shared primary key. Well, we will definitely need an example ;) - Suppose that we have two entities: Primus and Secundus. Both entities have primary key using Long Java type. They are related 1-1, and Secundus should use the same primary key as Primus. Let's start with following state of Primus:
@Entity
@Table(name = "PRIMUS")
public class Primus {

    private Long id;
    private String name;
    private Secundus secundus;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    @OneToOne(cascade = CascadeType.PERSIST, mappedBy = "primus")
    public Secundus getSecundus() {
        return secundus;
    }
...
}
Primus is more important from our point of view, it should be persisted first, and then Secundus, therefore we define the relation between them using mappedBy attribute. We also use cascading here to be able to persist both entities at once. Note, that we will only try to persist the entities, that's why there is only one type of cascade.

Let's take a look at Secundus now:
@Entity
@Table(name = "SECUNDUS")
public class Secundus {

    private Long id;
    private String name;
    private Primus primus;

    @Id
    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    @OneToOne
    @PrimaryKeyJoinColumn
    public Primus getPrimus() {
        return primus;
    }
...
}
Note, that we don't define the method of generating Secundus identifier here. We also use Secundus identifier (primary key) as the join column for the primus property (see @PrimaryKeyJoinColumn).

Let's try to test the persisting of those two with following code:
 ... 
    @PersistenceContext
    private EntityManager entityManager;

    @Test
    @Transactional(readOnly = false)
    public void test01() {
        final Primus primus = new Primus();
        final Secundus secundus = new Secundus();
        primus.setSecundus(secundus);
        entityManager.persist(primus);
    }
...
The above code and our current JPA mappings will lead to the following SQL queries (used database is MySQL 5.5):
INSERT INTO PRIMUS (NAME) VALUES (null)
SELECT LAST_INSERT_ID()
INSERT INTO SECUNDUS (ID, NAME) VALUES (null, null)
and of course fail at the second insert.

As you probably suppose the Primus identifier is set right after the select last_insert_id() query is performed, let's try to use this knowledge to initialize the Secundus identifier - we will modify the Primus method setId as visible below:
    public void setId(Long id) {
        this.id = id;
        if (null != secundus) {
            secundus.setId(id);
        }
    }
This time the queries looks good:
INSERT INTO PRIMUS (NAME) VALUES (null)
SELECT LAST_INSERT_ID()
INSERT INTO SECUNDUS (ID, NAME) VALUES (12, null)
and both entities are written into database :).

At this point I would consider one more modification of Primus - which is not required for entities persisting, but will assure that both entities are bound in proper way:
    public void setSecundus(Secundus secundus) {
        this.secundus = secundus;
        if (null != secundus) {
            secundus.setPrimus(this);
        }
    }

I'm curious if you have any other solution for this situation, feel free to share it with me :)

PS: The above solution does work for following JPA providers: EclipseLink (2.1) or Hibernate (3.4), but doesn't work for OpenJPA (2.1).



For JPA 2.0+ based solution see my post: @OneToOne with shared primary key, revisited :)

Sunday, April 10, 2011

JPA and unmodifiable collections

Patrycja Węgrzynowicz (see Yon Labs / Yon Consulting) had very interesting talk on 33rd Degree Conference this year called "Patterns and Anti-Patterns in Hibernate". Inspired by this talk, I decided to verify JPA providers behavior regarding unmodifiable Collections.

I will use the same Employer - Employee - Benefit model as in my previous JPA posts (ex. JPA Demystified (episode 1) - @OneToMany and @ManyToOne mappings).

Let's take a look at the Employer entity:
@Entity
@Table(name = "EMPLOYERS")
public class Employer implements Serializable {
    ...
    private List<Employee> employees = new ArrayList<Employee>();
    ...

    public void addEmployee(Employee employee) {
        employee.setEmployer(this);
        employees.add(employee);
    }
    ...
    @OneToMany(mappedBy = "employer", cascade = CascadeType.PERSIST)
    public List<Employee> getEmployees() {
        return Collections.unmodifiableList(employees);
    }
    ...
    public void setEmployees(List<Employee> employees) {
        this.employees = employees;
    }
    ...
}
As you see addEmployee and getEmployees method usage should protect the employees list from the modifying outside the Employer.

Let's use following test:
    @Test
    @Transactional(readOnly = false)
    public void test03() throws Exception {
        Employer employer = employerDAO.get(1L);
        assertEquals(3, employer.getEmployees().size());
        employerDAO.merge(employer);
    }
Nothing special, we fetch the Employer from database, doesn't change anything, and try to merge the state into database. Simple, isn't it? Well, when you try to use Hibernate as JPA provider you'll encounter:
java.lang.UnsupportedOperationException
 at java.util.Collections$UnmodifiableCollection.clear(Collections.java:1037)
 at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:501)
 at org.hibernate.type.CollectionType.replace(CollectionType.java:574)
 at org.hibernate.type.TypeFactory.replace(TypeFactory.java:505)
 at org.hibernate.event.def.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:392)
 at org.hibernate.event.def.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:200)
 at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:173)
 at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:81)
 at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:704)
 at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:688)
 at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:692)
 at org.hibernate.ejb.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:235)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
 at $Proxy29.merge(Unknown Source)
 at [...].dao.jpa.DefaultEmployerDAO.merge(DefaultEmployerDAO.java:22)
 at [...].dao.EmployerDAOTest.test03(EmployerDAOTest.java:76) 
Good Lord, why it is trying to clear the collection!? Patrycja mentioned in her talk, that when you return the unmodifiable view of collection, Hibernate will treat the property as "dirty" (because both collections are different as objects) and try to persist the changes - which in this case means deleting all collection elements and persist same collection elements again!! ...

OK, maybe OpenJPA will behave better ...
java.lang.IllegalAccessError: class org.apache.openjpa.util.java$util$Collections$UnmodifiableRandomAccessList$0$proxy cannot access its superclass java.util.Collections$UnmodifiableRandomAccessList
 at java.lang.ClassLoader.defineClass1(Native Method)
 at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
 at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
 at java.lang.ClassLoader.defineClass(ClassLoader.java:466)
 at serp.bytecode.BCClassLoader.findClass(BCClassLoader.java:50)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
 at java.lang.Class.forName0(Native Method)
 at java.lang.Class.forName(Class.java:247)
 at org.apache.openjpa.util.GeneratedClasses.loadBCClass(GeneratedClasses.java:67)
 at org.apache.openjpa.util.ProxyManagerImpl.getFactoryProxyCollection(ProxyManagerImpl.java:363)
 at org.apache.openjpa.util.ProxyManagerImpl.newCollectionProxy(ProxyManagerImpl.java:189)
 at org.apache.openjpa.kernel.StateManagerImpl.newFieldProxy(StateManagerImpl.java:1824)
 at org.apache.openjpa.kernel.StateManagerImpl.newProxy(StateManagerImpl.java:1790)
 at org.apache.openjpa.jdbc.meta.strats.StoreCollectionFieldStrategy.load(StoreCollectionFieldStrategy.java:543)
 at org.apache.openjpa.jdbc.meta.FieldMapping.load(FieldMapping.java:934)
 at org.apache.openjpa.jdbc.kernel.JDBCStoreManager.load(JDBCStoreManager.java:691)
 at org.apache.openjpa.kernel.DelegatingStoreManager.load(DelegatingStoreManager.java:117)
 at org.apache.openjpa.kernel.ROPStoreManager.load(ROPStoreManager.java:78)
 at org.apache.openjpa.kernel.StateManagerImpl.loadFields(StateManagerImpl.java:3047)
 at org.apache.openjpa.kernel.StateManagerImpl.loadField(StateManagerImpl.java:3121)
 at org.apache.openjpa.kernel.StateManagerImpl.beforeAccessField(StateManagerImpl.java:1606)
 at org.apache.openjpa.kernel.StateManagerImpl.accessingField(StateManagerImpl.java:1591)
 at org.apache.openjpa.enhance.RedefinitionHelper.accessingField(RedefinitionHelper.java:59)
 at org.apache.openjpa.enhance.[...]$entities$domain$Employer$pcsubclass.getEmployees(Unknown Source)
 at [...].dao.EmployerDAOTest.test03(EmployerDAOTest.java:75)
Nope :(. OpenJPA fails much sooner - it is unable to fetch the Employees list!

Our last hope - EclipseLink ... Works :) it performs appropriate selects, to fetch the entity, then on merge verifies that there were no changes in it, and simply does nothing.

Of course all those 3 providers work perfectly when you simply return the collection, not the unmodifiable view.

Monday, March 21, 2011

Useless Flow Managed Persistence

Spring Webflow has some interesting feature called Flow Managed Persistence. In short: it allows you to change some persistent entity during the flow, and merge the state of entity into persistence context at the end of the flow.

Sounds interesting, and is worth trying for sure :) - but you have to be aware, that Spring Webflow guys have overlooked something very important. The JpaFlowExecutionListener, which is involved in the Flow Managed Persistence handling, binds an Entity Manager instance to the flow scope, when a flow execution starts.

So what? - you may say :) - hm, let's look at the Spring Webflow documentation: "any objects stored in flow scope need to be Serializable" - and now back to the Entity Manager interface - nope - it doesn't extend Serializable, so we have no guarantee that it will be.

Indeed when you use the EclipseLink as JPA provider you'll get beautiful "SnapshotCreationException: Could not serialize flow execution; make sure all objects stored in flow or flash scope are serializable".

I've reported this problem in Spring Webflow JIRA - see JpaFlowExecutionListener shouldn't assume that EntityManager is Serializable

You may also find this problem in Community Forum - EclipseLink, Toplink NotSerializableException - mentioned first on August 28th, 2008.

I'm waiting eagerly for the fix :)

Tuesday, December 7, 2010

JPA - insert instead of update

Few days ago I was digging through the code in some project to find the reason why EclipseLink (2.1.1) is performing database insert instead of update. Let me say, that it was the hardest thing to find in my whole JPA-using developer career - so for the records, and to let you find it faster than me ;)

There was an association between entities Alpha and Beta, made in following manner:
public class Alpha ... {
    ...
    private Beta beta;

    @ManyToOne(..., optional = false)
    @JoinColumn(name = "beta_id")
    @JoinFetch(JoinFetchType.INNER)
    public Beta getBeta() { 
        return beta;
    }
    ...
}
I wrote about the @JoinFetch annotation in one of my previous posts already (see: JPA Demystified (episode 1) - @OneToMany and @ManyToOne mappings) - it is used here, to fetch the associated Beta along with the Alpha, in one query.

In the project analyzed by me, instance of Alpha was created at one point, edited in few steps, with persisting into database between the steps. Everything was fine after the first step. Alpha was written into database (first insert), but after the second step, primary key violation occurred each time I tested it (same entity was again inserted into DB, while it should be updated at this point).

My first hint was missing equals and hashcode methods on Alpha and Beta. I've added it, but it doesn't helped at all. So I meet with the best friend of all Developers - Debugger :) - and started to dig into the project and EclipseLink source code. At this point let me give you first suggestion - when you trace the JPA problems use the Debugger and verify performed database operations in parallel, because only both those things can give you full view of the problem lurking beneath the Application's surface ;)

Watching the SQL queries, I've found that EclipseLink is performing query fetching the Alpha along with the Beta (as directed by @JoinFetch annotation), right before the invalid insert. Matching this operation to the Java code, I've found that EclipseLink is trying to check what has changed in the Alpha entity, to perform only the required updates to the database. The SQL query was using inner join ...

Do you suppose what was wrong? :) - There was no Beta entity associated to the Alpha, and therefore this query - "select ... from Alpha inner join Beta ..." was returning no records at all. At this point EclipseLink was assuming that Alpha entity was not written into database yet, and tried to do it again - Alpha has already generated identifier (while first insert into database), and ... beautiful exception :)

What you should learn from the above situation is that optional attribute of association definition is informational only! - It's provider choice to throw an exception if the associated value will be null, or do something completely else, as you see EclipseLink developers are ignoring it silently :(

Last but not least - use @JoinFetch(JoinFetchType.INNER) if and only if you are absolutely sure, that associated entity will always exist - ex. when the database scheme doesn't allow other possibility. Don't trust JPA provider completely ;)

Sunday, December 5, 2010

JPA - Lazy Loading and View rendering

JPA would be unusable without Lazy Loading, which is a feature allowing fetching of correlated entities from the database when they are referenced for the first time, rather than immediately when you perform the database query. Imagine that you cannot use Lazy Loading ... One query for the single result can return the whole database in this case, if you are lucky enough ;)

Each time when you use @OneToMany or @ManyToMany annotations, Lazy Loading is active by default, without any effort. For all other types of associations you may control it, using fetch attribute of annotation defining the association.

Sounds wonderful, isn't it? Everything is done automagically without my intervention, required entities are fetched from the database when they are needed, the world is beautiful and free of violence ... Well, not quite, my dear Watson ...

You have to be aware, that some entities may be referenced for the first time at the View rendering level. For Spring Framework based application with JSP used for View layer (but not only in this setup) this can lead to miscellaneous problems, which depends on used JPA provider.

Let's take a deeper look at the potential problems, using usual Employer - Employee - Benefit example from my previous JPA related posts. Suppose that we want to fetch the Employer information from the database, but the Employees and Benefits only when they are referenced. Associations between Employer, Employees and Benefits look like this:
@Entity
@Table(name = "EMPLOYERS")
public class Employer implements Serializable {
    ...
    private List<Employee> employees;
    ...
    @OneToMany(mappedBy = "employer")
    public List<Employee> getEmployees() {
        return employees;
    }
    ...
}

@Entity
@Table(name = "EMPLOYEES")
public class Employee implements Serializable {
    ...
    private List<Benefit> benefits;
    private Employer employer;
    ...
    @OneToMany(mappedBy = "employee")
    public List<Benefit> getBenefits() {
        return benefits;
    }

    @ManyToOne
    @JoinColumn(name = "EMPLOYER_ID")
    public Employer getEmployer() {
        return employer;
    }
    ...
}

@Entity
@Table(name = "BENEFITS")
public class Benefit implements Serializable {
    ...
    private Employee employee;

    @ManyToOne
    @JoinColumn(name = "EMPLOYEE_ID")
    public Employee getEmployee() {
        return employee;
    }
    ...
}
DAO fetching the Employer data for Model:
@Repository
public class DefaultEmployerDAO implements EmployerDAO {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public Employer get(Long identifier) {
        return entityManager.find(Employer.class, identifier);
    }
    ...
}
And the JSP view displaying the Employer information, along with the Employees, and their Benefits:
<h1><c:out value="${employer.businessName}" /></h1>
...
<c:forEach items="${employer.employees}" var="e">
    <tr>
        <td><c:out value="${e.firstName}" /></td>
        <td>
            <ul>
               <c:forEach items="${e.benefits}" var="b">
                   <li><c:out value="${b.name}" /></li>
               </c:forEach>
            </ul>
        </td>
    </tr>     
</c:forEach>
Let's examine how it will behave for different JPA providers, starting from EclipseLink 2.1.1. When we try to open desired view in browser, we will see something like this:


Application activity while preparing the Model (seen from the Spring Insight):


and while rendering the view itself:


All the SQL queries visible at the view rendering level are caused by Lazy Loading.
As you see the EclipseLink and Lazy Loading works without any special effort :)

Now it's time to check Hibernate 3.4.0.GA. For this provider, when we try to open the desired view in browser, we will see beautiful
LazyInitializationException: failed to lazily initialize a collection of role: [...].entities.domain.Employer.employees, no session or session was closed
Sweet, isn't it? :) - old hibernate session closed problem strikes again, this time in JPA ;) - but believe me, this is not the worst problem you may encounter using JPA ;), because with a little help from OpenEntityManagerInViewFilter it can be quickly corrected (if you are using hibernate for a little longer, you are probably well aware of very useful OpenSessionInViewFilter used in similar situations with "pure" hibernate). Let's add this filter to the web.xml:
<filter>
    <filter-name>OpenEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
    <init-param>
        <param-name>entityManagerFactoryBeanName</param-name>
        <param-value>persistence.EntityManagerFactory</param-value>
    </init-param>
</filter>
...
<filter-mapping>
    <filter-name>OpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
The entityManagerFactoryBeanName parameter should match the name of your Entity Manager factory bean in Spring Framework configuration.

With this filter we can get finally the desired results. This time Application activity while preparing the Model looks like this:

and while the view rendering:

Finally OpenJPA 2.0.1 - first try without the OpenEntityManagerInViewFilter, and we will see in browser:

Good Lord! No data loaded at all ... no exceptions reported ... the worst situation, because you don't know if there are no data to view in the database, or there is something wrong with the application! :(

Let's add the OpenEntityManagerInViewFilter, and try again:

Now it looks as expected!
Application activity while preparing the Model:


and the view rendering:


As you see above, Lazy Loading requires for both Hibernate and OpenJPA OpenEntityManagerInViewFilter to be used, while EclipseLink work without any special effort.

Saturday, November 20, 2010

JPA - @Enumerated default attribute

Sometimes you have to store Java Enum value into database, using JPA. As we all know it is pretty simple in fact, using @Enumerated annotation, like in the example below:
public enum BenefitType { 
    GROUP_TERM_LIFE, HEALTH_COVERAGE, LEGAL_ASSISTANCE, RETIREMENT_PLAN, TRANSPORTATION
}

public class Benefit implements Serializable {
    ...
    private BenefitType type;
    ...
    @Column(name = "BENEFIT_TYPE")
    @Enumerated
    public BenefitType getType() {
        return type;
    }
    ...
}
Yes, pretty simple, but also pretty perfidious :). Why? Because developers very often use the default values for annotation attributes, and @Enumerated attribute named value is by default EnumType.ORDINAL.

Good Lord! This means that JPA provider will use the Enum's ordinal method result as the value stored into database! Exactly, my dear Watson ... I believe you know, what it means ... one day, when some of the developers will add a new Enum value to the list of previously defined, or even sort the existing values, and thus change their order, the ordinal method may return completely different value than expected!

Whoever was the creator of @Enumerated annotation he/she didn't read the Enum's ordinal method JavaDoc, where we can find clear declaration:Most programmers will have no use for this method. It is designed for use by sophisticated enum-based data structures, such as EnumSet and EnumMap.

My conclusion is:
  • Do not add to your API anything which can cause unpredictable behavior of the code, and thus is useless in real world
  • Do not make such a thing the default value for anything - there will be hundreds of people who will use the default value
  • Read the documentation for the code used by you, repeatedly, as long as you understand it 

Thursday, September 30, 2010

JPA Demystified (episode 2) - Persisting entities

Let's go back to the example described in previous post - JPA Demystified (episode 1). We will take a deeper look into cascading of basic operations on entities.

We will stick with MySQL (version 5.1) as the database used for our tests. To be able to create the new records in the database, we have to adjust the table definitions from JPA Demystified (episode 1) a little, to allow usage of MySQL's AUTO_INCREMENT feature (see the end of this post).

Let's start with persisting the entity, for example Employer, with one Employee having one Benefit. This way we should trigger inserts into 3 database tables (each entity is stored in separate table).

We will create the entities for our tests in following manner:
    Employer employer = new Employer();  
    // ... set the employer properties ...  
    Employee employee = new Employee();  
    employee.setEmployer(employer);  
    // ... set the employee properties ...   
    Benefit benefit = new Benefit();  
    benefit.setEmployee(employee);  
    // ... set the benefit properties ...
    employee.setBenefits(Arrays.asList(benefit));
    employer.setEmployees(Arrays.asList(employee));
and try to persist it using following DAO:
public class DefaultEmployerDAO implements EmployerDAO {

    @PersistenceContext
    private EntityManager entityManager;

    public void persist(Employer employer) {
        entityManager.persist(employer);
    }
    ...
}
When you look at the tables definitions, you realize that we will need the primary key value to be set for each entity stored into database. Entity identifiers can be set by hand, if you want the full control over it, or generated automatically, which can be achieved using @GeneratedValue annotation. For example:
    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }
You have to be aware that default strategy for the primary key generation depends on the JPA provider and database used. For MySQL it can be AUTO_INCREMENT usage (Hibernate) or sequence table (EclipseLink, OpenJPA). As you see using default value isn't as consistent across the JPA providers as we would like it to be. Let's make it more predictable ;)
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() {
        return id;
    }
Having the above on each entity identifier will force all three providers to use the MySQL's AUTO_INCREMENT feature for generating primary keys.

You shouldn't smile yet ;) This is not the end of our story ... When you try to run persist method for the Employer now, you will encounter another inconsistency between the JPA providers. Only Hibernate is writing anything into database (into EMPLOYER table only), while both EclipseLink and OpenJPA complaints about the problems with not persisted objects bound to Employer. Why? - because we didn't used the cascade attribute for any of @OneToMany annotations yet, and thus all entities related to the Employer will not be persisted automatically.
We have to change the correlation between entities for Employer
    @OneToMany(mappedBy = "employer", cascade = CascadeType.PERSIST)
    public List getEmployees() {
        return employees;
    }
and for the Employee
    @OneToMany(mappedBy = "employee", cascade = CascadeType.PERSIST)    
    public List getBenefits() {
        return benefits;
    }
and now we can smile watching the records being written into database
-- Hibernate 3.4.0.GA
insert into EMPLOYERS (BUSINESS_NAME) values ('Business Name')
insert into EMPLOYEES (EMPLOYER_ID, FIRST_NAME) values (7, 'Jack')
insert into BENEFITS (EMPLOYEE_ID, END_DATE, name, START_DATE, BENEFIT_TYPE) values (2, '', 'Car for everyone!', '2010-09-26', 'TRANSPORTATION')

-- EclipseLink 2.1.1
INSERT INTO EMPLOYERS (BUSINESS_NAME) VALUES ('Business Name')
SELECT LAST_INSERT_ID()
INSERT INTO EMPLOYEES (FIRST_NAME, EMPLOYER_ID) VALUES ('Jack', 8)
SELECT LAST_INSERT_ID()
INSERT INTO BENEFITS (START_DATE, NAME, END_DATE, BENEFIT_TYPE, EMPLOYEE_ID) VALUES ('2010-09-26', 'Car for everyone!', '', 'TRANSPORTATION', 3)
SELECT LAST_INSERT_ID()

-- OpenJPA 2.0.1
INSERT INTO EMPLOYERS (BUSINESS_NAME) VALUES ('Business Name')
INSERT INTO EMPLOYEES (FIRST_NAME, EMPLOYER_ID) VALUES ('Jack', 9)
INSERT INTO BENEFITS (END_DATE, name, START_DATE, BENEFIT_TYPE, EMPLOYEE_ID) VALUES ('', 'Car for everyone!', '2010-09-26', 'TRANSPORTATION', 4)
As you see in this post, persist will store your entity into database, generating primary keys if requested, and updating the entities with the generated keys. Persisting entities is cascaded according to the cascade attribute of annotations describing correlations between entities. This attribute holds an array of cascade types, if one of the array elements is CascadeType.ALL or CascadeType.PERSIST the cascading will occur.

And as usual - the most boring part for the dessert :)

CREATE TABLE BENEFITS (  
   ID           INT UNSIGNED NOT NULL AUTO_INCREMENT,  
   EMPLOYEE_ID  INT UNSIGNED NOT NULL,  
   BENEFIT_TYPE VARCHAR(64) NOT NULL,  
   START_DATE   DATE,  
   END_DATE     DATE,  
   NAME         VARCHAR(128),   
   CONSTRAINT BENEFITS_PK PRIMARY KEY (ID)  
 );  
   
 CREATE TABLE EMPLOYEES (  
   ID          INT UNSIGNED NOT NULL AUTO_INCREMENT,  
   EMPLOYER_ID INT UNSIGNED NOT NULL,   
   FIRST_NAME  VARCHAR(64),   
   CONSTRAINT EMPLOYEES_PK PRIMARY KEY (ID)  
 );  
   
 CREATE TABLE EMPLOYERS (  
   ID            INT UNSIGNED NOT NULL AUTO_INCREMENT,   
   BUSINESS_NAME VARCHAR(128),   
   CONSTRAINT EMPLOYERS_PK PRIMARY KEY (ID)  
 );     

Saturday, September 25, 2010

JPA Demystified (episode 1) - @OneToMany and @ManyToOne mappings

Let's play with some @OneToMany and @ManyToOne mappings :) As you may read in wise books or boring javadocs, they serve for mapping 1-* and *-1 relations respectively.

Let's dig a little more into it using following example: Employer (1-*) Employee (1-*) Benefit (See below for DB tables and their content). Entity model for this situation will look like this:
@Entity
@Table(name = "EMPLOYERS")
public class Employer implements Serializable {

    private String businessName;
    private List<Employee> employees;
    private Long id;

    @Column(name = "BUSINESS_NAME")
    public String getBusinessName() {
        return businessName;
    }

    @OneToMany(mappedBy = "employer")
    public List<Employee> getEmployees() {
        return employees;
    }

    @Id
    public Long getId() {
        return id;
    }

...
}

@Entity
@Table(name = "EMPLOYEES")
public class Employee implements Serializable {

    private List<Benefit> benefits;
    private Employer employer;
    private String firstName;
    private Long id;

    @OneToMany(mappedBy = "employee")
    public List<Benefit> getBenefits() {
        return benefits;
    }

    @ManyToOne
    @JoinColumn(name = "EMPLOYER_ID")
    public Employer getEmployer() {
        return employer;
    }

    @Column(name = "FIRST_NAME")
    public String getFirstName() {
        return firstName;
    }

    @Id
    public Long getId() {
        return id;
    }
...
}

@Entity
@Table(name = "BENEFITS")
public class Benefit implements Serializable {

    private Employee employee;
    private Date endDate;
    private Long id;
    private String name;
    private Date startDate;
    private BenefitType type;

    @ManyToOne
    @JoinColumn(name = "EMPLOYEE_ID")
    public Employee getEmployee() {
        return employee;
    }

    @Column(name = "END_DATE")
    @Temporal(TemporalType.DATE)
    public Date getEndDate() {
        return endDate;
    }

    @Id
    public Long getId() {
        return id;
    }

    @Basic
    public String getName() {
        return name;
    }

    @Column(name = "START_DATE")
    @Temporal(TemporalType.DATE)
    public Date getStartDate() {
        return startDate;
    }

    @Column(name = "BENEFIT_TYPE")
    @Enumerated(EnumType.STRING)
    public BenefitType getType() {
        return type;
    }
...
}
Time for real fun :) - let's query the DB using following DAO:
public class DefaultEmployeeDAO implements EmployeeDAO {

    @PersistenceContext
    private EntityManager entityManager;

    public Employee get(Long id) {
        return entityManager.find(Employee.class, id);
    }
...
}
Executed SQL queries depend on used JPA provider:
-- Hibernate 3.4.0.GA
select employee0_.id as id1_1_, employee0_.EMPLOYER_ID as EMPLOYER3_1_1_, employee0_.FIRST_NAME as FIRST2_1_1_, employer1_.id as id2_0_, employer1_.BUSINESS_NAME as BUSINESS2_2_0_ 
from EMPLOYEES employee0_ left outer join EMPLOYERS employer1_ on employee0_.EMPLOYER_ID=employer1_.id 
where employee0_.id = 1 

-- EclipseLink 2.1.1
SELECT ID, FIRST_NAME, EMPLOYER_ID FROM EMPLOYEES WHERE (ID = 1)
SELECT ID, BUSINESS_NAME FROM EMPLOYERS WHERE (ID = 1) 

-- OpenJPA 2.0.1
SELECT t1.id, t1.BUSINESS_NAME, t0.FIRST_NAME 
FROM EMPLOYEES t0 LEFT OUTER JOIN EMPLOYERS t1 ON t0.EMPLOYER_ID = t1.id 
WHERE t0.id = 1 

As you see @ManyToOne mapping causes fetching the referenced entity along with the referencing one (fetch type EAGER), while fetching entity mapped with @OneToMany is deferred until it will be accessed for the first time (fetch type LAZY).

Take a deeper look at the queries generated by EclipseLink. What the heck!? Why they don't use joins?! And what we can do about it?

This is the situation where @JoinFetch comes to the rescue - but be aware - this annotation is EclipseLink addition to JPA and hence makes your mappings not portable across the different providers! How can it be used? see example below:
    @ManyToOne
    @JoinColumn(name = "EMPLOYER_ID")
    @JoinFetch(JoinFetchType.OUTER)
    public Employer getEmployer() {
        return employer;
    }
After adding the @JoinFetch your EclipseLink SQL queries will turn into:
SELECT t1.ID, t1.FIRST_NAME, t1.EMPLOYER_ID, t0.ID, t0.BUSINESS_NAME FROM EMPLOYEES t1 LEFT OUTER JOIN EMPLOYERS t0 ON (t0.ID = t1.EMPLOYER_ID) WHERE (t1.ID = 1)

Let's back to the @OneToMany annotation for a while, and check how the providers will behave when we change the default fetch type to EAGER, in following way:
    @OneToMany(mappedBy = "employee", fetch = FetchType.EAGER)
    public List getBenefits() {
        return benefits;
    }
This time the SQL queries are:
-- Hibernate 3.4.0.GA
select employee0_.id as id1_2_, employee0_.EMPLOYER_ID as EMPLOYER3_1_2_, employee0_.FIRST_NAME as FIRST2_1_2_, benefits1_.EMPLOYEE_ID as EMPLOYEE6_4_, benefits1_.id as id4_, benefits1_.id as id0_0_, benefits1_.EMPLOYEE_ID as EMPLOYEE6_0_0_, benefits1_.END_DATE as END2_0_0_, benefits1_.name as name0_0_, benefits1_.START_DATE as START4_0_0_, benefits1_.BENEFIT_TYPE as BENEFIT5_0_0_, employer2_.id as id2_1_, employer2_.BUSINESS_NAME as BUSINESS2_2_1_ 
from EMPLOYEES employee0_ left outer join BENEFITS benefits1_ on employee0_.id=benefits1_.EMPLOYEE_ID left outer join EMPLOYERS employer2_ on employee0_.EMPLOYER_ID=employer2_.id 
where employee0_.id=1

-- EclipseLink 2.1.1 (without @JoinFetch)
SELECT ID, FIRST_NAME, EMPLOYER_ID FROM EMPLOYEES WHERE (ID = 1)
SELECT ID, BUSINESS_NAME FROM EMPLOYERS WHERE (ID = 1)
SELECT ID, START_DATE, NAME, END_DATE, BENEFIT_TYPE, EMPLOYEE_ID FROM BENEFITS WHERE (EMPLOYEE_ID = 1)

-- OpenJPA 2.0.1
SELECT t1.id, t1.BUSINESS_NAME, t0.FIRST_NAME, t2.EMPLOYEE_ID, t2.id, t2.END_DATE, t2.name, t2.START_DATE, t2.BENEFIT_TYPE 
FROM EMPLOYEES t0 LEFT OUTER JOIN EMPLOYERS t1 ON t0.EMPLOYER_ID = t1.id LEFT OUTER JOIN BENEFITS t2 ON t0.id = t2.EMPLOYEE_ID 
WHERE t0.id = 1 ORDER BY t2.EMPLOYEE_ID ASC
Both Hibernate and OpenJPA generate impressive queries :) - but in fact they are using the most efficient way to fetch all correlated entities.
Quick note for OpenJPA users - the above example leads to an exception while processing the results:
<openjpa-2.0.1-r422266:989424 nonfatal general error> org.apache.openjpa.persistence.PersistenceException: null
FailedObject: ... .entities.domain.Employee-1 [org.apache.openjpa.util.LongId] [java.lang.String]
 at org.apache.openjpa.kernel.BrokerImpl.find(BrokerImpl.java:986)
 at org.apache.openjpa.kernel.BrokerImpl.find(BrokerImpl.java:880)
 at org.apache.openjpa.kernel.DelegatingBroker.find(DelegatingBroker.java:223)
 at org.apache.openjpa.persistence.EntityManagerImpl.find(EntityManagerImpl.java:477)
    ...
Caused by: java.lang.NullPointerException
 at org.apache.openjpa.jdbc.kernel.JDBCStoreManager.setInverseRelation(JDBCStoreManager.java:469)
 at org.apache.openjpa.jdbc.kernel.JDBCStoreManager.initializeState(JDBCStoreManager.java:429)
 at org.apache.openjpa.jdbc.kernel.JDBCStoreManager.initialize(JDBCStoreManager.java:322)
It looks like an error in OpenJPA.

Back to mainstream :) - we can narrow the number of queries generated by EclipseLink using @JoinFetch again:
    @OneToMany(mappedBy = "employee", fetch = FetchType.EAGER)
    @JoinFetch(JoinFetchType.OUTER)
    public List getBenefits() {
        return benefits;
    }

    @ManyToOne
    @JoinColumn(name = "EMPLOYER_ID")
    @JoinFetch(JoinFetchType.OUTER)
    public Employer getEmployer() {
        return employer;
    }
The above will lead to one query (instead of 3):
SELECT DISTINCT t1.ID, t1.FIRST_NAME, t1.EMPLOYER_ID, t0.ID, t0.START_DATE, t0.NAME, t0.END_DATE, t0.BENEFIT_TYPE, t0.EMPLOYEE_ID, t2.ID, t2.BUSINESS_NAME 
FROM EMPLOYEES t1 LEFT OUTER JOIN BENEFITS t0 ON (t0.EMPLOYEE_ID = t1.ID) LEFT OUTER JOIN EMPLOYERS t2 ON (t2.ID = t1.EMPLOYER_ID) 
WHERE (t1.ID = 1)

Finally, let's focus on optional attribute of @ManyToOne annotation. In our example both Employee and Benefit cannot exist without the Employer and Employee respectively, therefore we can change the mapping to:
    @ManyToOne(optional = false)
    @JoinColumn(name = "EMPLOYER_ID")
    public Employer getEmployer() {
        return employer;
    }
for the Employee, and:
    @ManyToOne(optional = false)
    @JoinColumn(name = "EMPLOYEE_ID")
    public Employee getEmployee() {
        return employee;
    }
for the Benefit.
This change will generate queries:
-- Hibernate 3.4.0.GA
select employee0_.id as id1_2_, employee0_.EMPLOYER_ID as EMPLOYER3_1_2_, employee0_.FIRST_NAME as FIRST2_1_2_, benefits1_.EMPLOYEE_ID as EMPLOYEE6_4_, benefits1_.id as id4_, benefits1_.id as id0_0_, benefits1_.EMPLOYEE_ID as EMPLOYEE6_0_0_, benefits1_.END_DATE as END2_0_0_, benefits1_.name as name0_0_, benefits1_.START_DATE as START4_0_0_, benefits1_.BENEFIT_TYPE as BENEFIT5_0_0_, employer2_.id as id2_1_, employer2_.BUSINESS_NAME as BUSINESS2_2_1_ 
from EMPLOYEES employee0_ left outer join BENEFITS benefits1_ on employee0_.id=benefits1_.EMPLOYEE_ID inner join EMPLOYERS employer2_ on employee0_.EMPLOYER_ID=employer2_.id where employee0_.id=1

-- EclipseLink 2.1.1 (without @JoinFetch)
SELECT ID, FIRST_NAME, EMPLOYER_ID FROM EMPLOYEES WHERE (ID = 1)
SELECT ID, BUSINESS_NAME FROM EMPLOYERS WHERE (ID = 1)
SELECT ID, START_DATE, NAME, END_DATE, BENEFIT_TYPE, EMPLOYEE_ID FROM BENEFITS WHERE (EMPLOYEE_ID = 1)

-- OpenJPA 2.0.1
SELECT t1.id, t1.BUSINESS_NAME, t0.FIRST_NAME, t2.EMPLOYEE_ID, t2.id, t2.END_DATE, t2.name, t2.START_DATE, t2.BENEFIT_TYPE FROM EMPLOYEES t0 INNER JOIN EMPLOYERS t1 ON t0.EMPLOYER_ID = t1.id LEFT OUTER JOIN BENEFITS t2 ON t0.id = t2.EMPLOYEE_ID WHERE t0.id = 1 ORDER BY t2.EMPLOYEE_ID ASC
Don't you think that default behavior of EclipseLink is a little strange? What would we do without the @JoinFetch? When we add it in the following way:
    @OneToMany(mappedBy = "employee", fetch = FetchType.EAGER)
    @JoinFetch(JoinFetchType.OUTER)
    public List getBenefits() {
        return benefits;
    }

    @ManyToOne(optional = false)
    @JoinColumn(name = "EMPLOYER_ID")
    @JoinFetch(JoinFetchType.INNER)
    public Employer getEmployer() {
        return employer;
    }
The world becomes beautiful again:
SELECT DISTINCT t1.ID, t1.FIRST_NAME, t1.EMPLOYER_ID, t0.ID, t0.BUSINESS_NAME, t2.ID, t2.START_DATE, t2.NAME, t2.END_DATE, t2.BENEFIT_TYPE, t2.EMPLOYEE_ID 
FROM EMPLOYEES t1 LEFT OUTER JOIN BENEFITS t2 ON (t2.EMPLOYEE_ID = t1.ID), EMPLOYERS t0 
WHERE ((t1.ID = 1) AND (t0.ID = t1.EMPLOYER_ID))
I know, you are already tired reading the above thoughts ;) - so let's finish for now - we will return to the @OneToMany and @ManyToOne soon with some additional deliberation on cascading and a little more complicated DB examples.

The boring part now ;) - read it at your own risk ;) - DB tables and their content (MySQL targeted)
CREATE TABLE BENEFITS (
    ID            NUMERIC     NOT NULL,
    EMPLOYEE_ID   NUMERIC     NOT NULL,
    BENEFIT_TYPE  VARCHAR(64) NOT NULL,
    START_DATE    DATE,
    END_DATE      DATE,
    NAME          VARCHAR(128), 
    CONSTRAINT BENEFITS_PK PRIMARY KEY (ID)
);

CREATE TABLE EMPLOYEES (
    ID           NUMERIC NOT NULL,
    EMPLOYER_ID  NUMERIC NOT NULL, 
    FIRST_NAME   VARCHAR(64), 
    CONSTRAINT EMPLOYEES_PK PRIMARY KEY (ID)
);

CREATE TABLE EMPLOYERS (
    ID             NUMERIC NOT NULL, 
    BUSINESS_NAME  VARCHAR(128), 
    CONSTRAINT EMPLOYERS_PK PRIMARY KEY (ID)
);

INSERT INTO EMPLOYERS (ID, BUSINESS_NAME) VALUES (1, 'Mighty Ducks');

INSERT INTO EMPLOYEES (ID, EMPLOYER_ID, FIRST_NAME) VALUES (1, 1, 'John');
INSERT INTO EMPLOYEES (ID, EMPLOYER_ID, FIRST_NAME) VALUES (2, 1, 'Mary');
INSERT INTO EMPLOYEES (ID, EMPLOYER_ID, FIRST_NAME) VALUES (3, 1, 'Eugene');

INSERT INTO BENEFITS (ID, EMPLOYEE_ID, BENEFIT_TYPE, START_DATE, END_DATE, NAME)
    VALUES(1, 1, 'HEALTH_COVERAGE', '2005-01-01', null, 'Healthy Employees');
INSERT INTO BENEFITS (ID, EMPLOYEE_ID, BENEFIT_TYPE, START_DATE, END_DATE, NAME)
    VALUES(2, 1, 'RETIREMENT_PLAN', '2005-01-01', null, 'Gold Autumn');    
INSERT INTO BENEFITS (ID, EMPLOYEE_ID, BENEFIT_TYPE, START_DATE, END_DATE, NAME)
    VALUES(3, 1, 'GROUP_TERM_LIFE', '2005-01-01', null, 'Always Secured');