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.

Sunday, November 28, 2010

Spring MVC pitfall - overriding default beans

We all learn the whole life :) My last lesson was related to overriding default beans registered by mvc namespace parsers. It all started when I read Customizing WebDataBinder initialization section of Spring Framework 3.0 documentation. You can read there something like this:
To externalize data binding initialization, you can provide a custom implementation of the WebBindingInitializer interface, which you then enable by supplying a custom bean configuration for an AnnotationMethodHandlerAdapter, thus overriding the default configuration.
Encouraged by this citation I registered my own bean along the usual <mvc:annotation-driven />
<mvc:annotation-driven />

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="webBindingInitializer" ref="MyWebBindingInitializer" />
</bean>
    
<bean class="[...].DefultWebBindingInitializer" id="MyWebBindingInitializer" />
At this point I've encountered first problem - the order of beans registration matters in the above case - first registered handler will be used. This is reasonable, one AnnotationMethodHandlerAdapter is registered by Spring while processing <mvc:annotation-driven /> and one by me. First registered wins :)

I've moved my bean before <mvc:annotation-driven /> and started enjoying its work. After few days I was preparing some functionality returning one value from the handler method, which should be converted automagically to JSON response (see my posts: JSON - Jackson to the rescue and JSON - Jackson Serialization Narrowed). To my surprise, usual configuration didn't worked, I started digging through my own and Spring Framework code, and found the reason (which in fact can be also deduced from mvc:annotation-driven documentation) - my own bean didn't registered the same message converters as parser responsible for processing mvc:annotation-driven (AnnotationDrivenBeanDefinitionParser) and especially one needed by me: MappingJacksonHttpMessageConverter.

At this point I had few possibilities to solve this problem, I could for example register appropriate message converter myself, but I decided to go a different way, because solving the problems generated ourselves, is worse than avoiding those problems :)

I've prepared BeanPostProcessor implementation correcting the one property required by me:
public class MVCConfigurationPostProcessor implements BeanPostProcessor {

    private WebBindingInitializer webBindingInitializer;

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof AnnotationMethodHandlerAdapter) {
            ((AnnotationMethodHandlerAdapter) bean).setWebBindingInitializer(webBindingInitializer);
        }
        return bean;
    }

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }   

    public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
        this.webBindingInitializer = webBindingInitializer;
    }

}
Then I've corrected my Spring configuration to:
<bean class="[...].MVCConfigurationPostProcessor">
    <property name="webBindingInitializer" ref="MyWebBindingInitializer" />
</bean>
    
<bean id="MyWebBindingInitializer" class="[...].DefultWebBindingInitializer" />
    
<mvc:annotation-driven />
Finally I was able to enjoy both AnnotationMethodHandlerAdapter using my own WebBindingInitializer and JSON conversion working correctly :).

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 

Saturday, November 13, 2010

JMX - Counting Users

Have you ever asked yourself how many users are using your favorite service at the moment? Watching the leaves falling down outside your window you ask yourself - am I alone? Is there anyone logged in except me? Hey! Stop watching the leaves, they will only make you more melancholic ;) - use the JMX :)

Read this article, and you'll discover the idea of controlling number of users logged in for web application based on Spring FrameworkSpring Security and Java Management Extensions (JMX).

First of all, we'll define the Managed Bean:
@ManagedResource(objectName = "Test:name=BigBrother")
public class BigBrother implements NotificationPublisherAware {

    private NotificationPublisher notificationPublisher;

    private final AtomicInteger userCounter = new AtomicInteger(0);

    @ManagedAttribute
    public int getUserCounter() {
        return userCounter.intValue();
    }

    @Override
    public void setNotificationPublisher(NotificationPublisher notificationPublisher) {
        this.notificationPublisher = notificationPublisher;
    }

    public void userLoggedIn(Principal principal) {
        userCounter.incrementAndGet();
        notificationPublisher.sendNotification(new Notification("User '" + principal.getName() + "' logged in.", this, 0));
    }

    public void userLoggedOut(Principal principal) {
        userCounter.decrementAndGet();
        notificationPublisher.sendNotification(new Notification("User '" + principal.getName() + "' logged out.", this, 0));
    }

}
Now we have to call the appropriate methods of this bean each time user will log in or log out.

First case is definitely the easiest one :) We may use the default behavior of Spring Security. Each time user is successfully authenticated, the Spring Security tries to publish an application event related to it. Happily for us when you use Security Namespace Configuration the default event publisher used (DefaultAuthenticationEventPublisher) sends the AuthenticationSuccessEvent. 
This type of events can be observed by the bean similar to:
public class LoginListener implements ApplicationListener<AuthenticationSuccessEvent> {

    @Autowired
    private BigBrother bigBrother;

    @Override
    public void onApplicationEvent(AuthenticationSuccessEvent event) {
        bigBrother.userLoggedIn(event.getAuthentication());
    }
}
All you have to do is register the above bean in Spring context :)

Now it's time to face the handling of user's log out. We could use nice feature of Spring Security LogoutFilter - each time user log out is handled by it, and the log out finishes successfully, the defined LogoutSuccessHandler is called. You may think - It can't be so easy - Exactly, my dear Watson, it can't be :)

You have to remember that users usually do what they want, and not what you expect them to do :) They don't have to log out from your application, then can just close the browser window, or shutdown the computer, or just leave the browser alone for so long that HTTP session will expire.

The expiration is the clue :) We can use HttpSessionBindingListener interface - if we add object implementing this interface to the HTTP session, it will be notified when it will be removed from session, which can be triggered when: a) session will be invalidated on user's log out (default behavior of  Spring Security configured using Security Namespace Configuration), b) session expires after some time of user inactivity, c) session attribute is removed on developer's demand

Let's start with the following XML configuration:
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">
    ...
    <security:http auto-config='true'>
        ...
        <security:custom-filter ref="security.BigBrotherHelpingFilter" after="SECURITY_CONTEXT_FILTER" />
        ...                
    </security:http>
    ...                            
    <bean id="security.BigBrotherHelpingFilter" class="....BigBrotherHelpingFilter" />
</beans>
The above XML will add our own authentication processing filter into Spring Security filters chain, right after the SecurityContextPersistenceFilter (see: Adding in Your Own Filters). It will be something like this:
public class BigBrotherHelpingFilter extends GenericFilterBean {

    private String attributeName = "__BIG_BROTHER_HELPER";

    @Autowired
    private BigBrother bigBrother;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
                    ServletException {
        chain.doFilter(request, response);
        if (((HttpServletResponse) response).isCommitted()) {
            return;
        }
        HttpSession httpSession = ((HttpServletRequest) request).getSession(false);
        if (null == httpSession) {
            return;
        }
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (null != authentication) {
            httpSession.setAttribute(attributeName, new AuthenticationHolder(authentication, bigBrother));
        } else {
            httpSession.setAttribute(attributeName, null);
        }
    }
    ...
}
And the last part of code for this post:
public class AuthenticationHolder implements HttpSessionBindingListener {

    private final Authentication authentication;

    private final BigBrother bigBrother;

    public AuthenticationHolder(Authentication authentication, BigBrother bigBrother) {
        this.authentication = authentication;
        this.bigBrother = bigBrother;
    }

    public Authentication getAuthentication() {
        return authentication;
    }

    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        // Ignore ...
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        bigBrother.userLoggedOut(authentication);
    }

}

Let's test the whole setup using VisualVM - when you run it, select your servlet container, and then available managed beans, under Test/BigBrother you should see the bean used by us:


Let's subscribe to the notifications in Notifications tab, and try to log in to our web application, you should see new notification:


When you select the Operations tab, and run getUserCounter operation you'll receive:

Now let's try to log out, then check the notifications:


and the user counter:
You can observe similar effect to log out when you leave your browser untouched until your HTTP session will expire (by default 30 minutes). 

Monday, November 1, 2010

JMX - Notification Publishing

In my previous post: JMX - Life Savior or Backdoor - I've prepared an example of JMX usage within the Spring Framework, let's modify it a little, to show the method of publishing notifications.

@ManagedResource(objectName = "Test:name=Cerberus")
public class Cerberus extends HandlerInterceptorAdapter implements NotificationPublisherAware {

    private NotificationPublisher notificationPublisher;

    private boolean opened = true;

    @ManagedOperation
    public void close() {
        this.opened = false;
        notificationPublisher.sendNotification(new Notification("Hades closed", this, 0));
    }

    @ManagedAttribute
    public boolean isOpened() {
        return opened;
    }

    @ManagedOperation
    public void open() {
        this.opened = true;
        notificationPublisher.sendNotification(new Notification("Hades opened", this, 0));
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!opened) {
            notificationPublisher.sendNotification(new Notification("Hades out of order", this, 0));
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
        }
        return opened;
    }

    public void setNotificationPublisher(NotificationPublisher notificationPublisher) {
        this.notificationPublisher = notificationPublisher;
    }

}

Again we will use the VisualVM for watching our managed bean. Run it, select tomcat on the left pane, switch to the MBeans tab, select Test and then Cerberus, and finally switch to the Notifications. You will see:
To watch the notifications coming from the managed bean you have to subscribe to them first, click on the "Subscribe" and switch to the Operations - run the close operation - and check what is visible in Notifications now:


Try to reach the URL to which the Cerberus is bound - you'll get 404 error in return (as expected), and another notification:


Running the open method will give you another one:


Now you may make yourself a cup of tea, and become a Big Brother for a while, watching the internals of your application ;)

Sunday, October 31, 2010

JMX - Life Savior or Backdoor

The Java Management Extensions (JMX) API is a standard for managing and monitoring applications and services. Its usage in application build using Spring Framework is very simple and easy. I hope you'll agree with me after lecture of this post.

We will use one of the possible solutions for JMX and Spring usage, based on Java 5 annotations. On the Spring configuration level we need only one new line:

<beans xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" 
xsi:schemalocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context 
           http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    ...
    <context:mbean-export />
    ...
</beans>

The rest of this magic is done at the Java source :). Let's build a handler interceptor, which will be also exported as the managed bean:

@ManagedResource(objectName = "Test:name=Cerberus")
public class Cerberus extends HandlerInterceptorAdapter {

    private boolean opened = true;

    @ManagedOperation
    public void close() {
        this.opened = false;
    }

    @ManagedAttribute
    public boolean isOpened() {
        return opened;
    }

    @ManagedOperation
    public void open() {
        this.opened = true;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!opened) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
        }
        return opened;
    }

}

Suppose that we map this interceptor to some URL within our web application, check if the target URL will return some response ...

Wonderful! But wait, where can we see this managed bean? - you may say :) - sometimes you need the glasses to read the newspaper ;) - We will use the VisualVM as the glasses, which will help us to see the managed beans. When we run it, we will see something like this:
Now select the Tomcat on the left pane, and when the right one will display the tomcat information, choose MBeans (Managed Beans)
From the list of Managed Beans choose Test and then Cerberus - why? We named our bean this way :) - using: @ManagedResource(objectName = "Test:name=Cerberus"), now you should see:
We used the @ManagedAttribute only on isOpened method, therefore the opened attribute will be read only. Let's switch to the Operations tab, which contains:
As you see we can run from this tab 3 methods marked with @ManagedOperation or @ManagedAttribute annotations. Let's run the close method - click on the close button and following dialog will appear:
Let's check the opened attribute using isOpened method call:
Wonderful, but what does it mean to our handler interceptor? Let's try to reach the URL protected by our Cerberus again:
It looks like the Cerberus stopped the request, sending 404 error - as we asked him to do :)

Amazing and terrifying - you can control your application behavior from outside - use it with care ...

Thursday, October 21, 2010

JSON - Jackson Serialization Narrowed

In my last post - JSON - Jackson to the rescue - I've described quick example of JSON response creating. Let's back to this example and take a deeper look at the created response.

[{"id":1, "benefits":[{"name":"Healthy Employees", "id":1, "type":"HEALTH_COVERAGE", "startDate":1104534000000, "endDate":null}, {"name":"Gold Autumn","id":2,"type":"RETIREMENT_PLAN","startDate":1104534000000,"endDate":null},{"name":"Always Secured","id":3,"type":"GROUP_TERM_LIFE","startDate":1104534000000,"endDate":null}],"firstName":"John"},{"id":2,"benefits":[],"firstName":"Mary"},{"id":3,"benefits":[],"firstName":"Eugene"}]

There are situations, where parts of the data represented by domain entities should remain invisible for the public, and therefore shouldn't be serialized and exposed via JSON. We will try to narrow our response down to make it a little more secure from our point of view - by hiding the Employee's benefits.

First method to achieve this goal is usage of @JsonIgnore annotation.

@Entity
@Table(name = "EMPLOYEES")
public class Employee implements Serializable {
   ...
    @JsonManagedReference("employee-benefit")
    @OneToMany(mappedBy = "employee", cascade = CascadeType.PERSIST)    
    @JsonIgnore
    public List getBenefits() {
        return benefits;
    }
    ...
}

The above modification will change the response to:

{"employees":[{"id":1,"firstName":"John"},{"id":2,"firstName":"Mary"},{"id":3,"firstName":"Eugene"}]}

which is exactly what we need - but don't smile too wide yet ;) - consider situation when you want the same entity to be serialized in different ways depending on the request "context". It is very probable that you would like give to customer only the properties necessary in the response, while the admin should see much more in his part of application. @JsonIgnore is unusable in this case :( - but wait, don't panic yet - here we come to the second solution - @JsonView annotation

Let's modify the controller preparing the JSON response in the following manner:

@Controller
@RequestMapping("/employee-list.json")
public class EmployeeListController {

    private static final String MODEL_KEY_EMPLOYEES = "employees";

    @Autowired
    private EmployerDAO employerDAO;

    private final ObjectMapper objectMapper = new ObjectMapper();

    private final MappingJacksonJsonView view = new MappingJacksonJsonView();

    public EmployeeListController() {
        objectMapper.getSerializationConfig().setSerializationView(Employer.PublicView.class);
        view.setObjectMapper(objectMapper);
        view.setRenderedAttributes(new HashSet(Arrays.asList(MODEL_KEY_EMPLOYEES)));
    }

    @RequestMapping(method = RequestMethod.GET)
    public View handleGet(@RequestParam("employerId") Long employerId, Model model) {
        model.addAttribute(MODEL_KEY_EMPLOYEES, employerDAO.getEmployees(employerId));
        return view;
    }
}

As you see we will use the Spring Framework's MappingJacksonJsonView, with our own ObjectMapper instance. In controller's constructor we define which model attributes should be serialized (using setRenderedAttributes method), and switch serialization view to the Employer.PublicView. This will force Jackson to serialize only those properties which don't have @JsonView annotation at all, or have @JsonView annotations matching specified view. If we will modify our entities to look like this:

@Entity
@Table(name = "EMPLOYERS")
public class Employer implements Serializable {

    public interface PrivateView { }

    public interface PublicView { }
    ...
    @Column(name = "BUSINESS_NAME")
    @JsonView(PublicView.class)
    public String getBusinessName() {
        return businessName;
    }

    @JsonManagedReference("employer-employee")
    @OneToMany(mappedBy = "employer", cascade = CascadeType.PERSIST)
    @JsonView(PublicView.class)
    public List getEmployees() {
        return employees;
    }

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

@Entity
@Table(name = "EMPLOYEES")
public class Employee implements Serializable {
    ...
    @JsonManagedReference("employee-benefit")
    @OneToMany(mappedBy = "employee", cascade = CascadeType.PERSIST)
    @JsonView(PrivateView.class)
    public List getBenefits() {
        return benefits;
    }

    @JsonBackReference("employer-employee")
    @ManyToOne(optional = false)
    @JoinColumn(name = "EMPLOYER_ID")
    public Employer getEmployer() {
        return employer;
    }

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

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

we will get exactly the same response as for @JsonIgnore usage, but in a little more flexible way :)

{"employees":[{"id":1,"firstName":"John"},{"id":2,"firstName":"Mary"},{"id":3,"firstName":"Eugene"}]}

The last possibility worth mentioning here is usage of mix-in annotations, let's change our controller once again to show the idea standing behind it.

@Controller
@RequestMapping("/employee-list.json")
public class EmployeeListController {

    @JsonIgnoreProperties("benefits")
    private static class EmployeePublicView extends Employee {
        // Empty by design ...
    }

    private static final String MODEL_KEY_EMPLOYEES = "employees";

    @Autowired
    private EmployerDAO employerDAO;

    private final ObjectMapper objectMapper = new ObjectMapper();

    private final MappingJacksonJsonView view = new MappingJacksonJsonView();

    public EmployeeListController() {
        objectMapper.getSerializationConfig().addMixInAnnotations(Employee.class, EmployeePublicView.class);
        view.setObjectMapper(objectMapper);
        view.setRenderedAttributes(new HashSet(Arrays.asList(MODEL_KEY_EMPLOYEES)));
    }

    @RequestMapping(method = RequestMethod.GET)
    public View handleGet(@RequestParam("employerId") Long employerId, Model model) {
        model.addAttribute(MODEL_KEY_EMPLOYEES, employerDAO.getEmployees(employerId));
        return view;
    }

}

As you see it is very similar to the controller above, the difference is new class EmployeePublicView extending Employee, and defining that benefits property will be ignored while serialization (using @JsonIgnoreProperties annotation). OK, but how this will help us with desired serialization of Employee instances? - the key factor is addMixInAnnotations usage - this method allows us to override the annotations defined on Employee class with those from the EmployeePublicView! Amazing and effective idea which doesn't need any domain entities modifications (you may safely remove the @JsonView annotations from the preview example)

Monday, October 18, 2010

JSON - Jackson to the rescue

Sometimes you have to fetch some data from the server in JavaScript, JSON is pretty good choice for this task.

Let's play with the Employer - Employee - Benefit example from the post JPA Demystified (episode 1) - @OneToMany and @ManyToOne mappings. We will use it inside the web application based on Spring Framework. Our first controller will return the employees list as the response body, in our case MappingJacksonHttpMessageConverter will be used automagically for converting the value returned by handleGet method to the response send to client.

@Controller
@RequestMapping("/employee-list.json")
public class EmployeeListController {
    @Autowired
    private EmployerDAO employerDAO;

    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public List<Employee> handleGet(@RequestParam("employerId") Long employerId) {
        return employerDAO.getEmployees(employerId);
    }
}

When we try to fetch the data for the first time, we encounter beautiful exception: JsonMappingException: Infinite recursion (StackOverflowError) - caused by bi-directional references between the Employer - Employee - Benefit.

Looking for the possible solution, I've found a note Handle bi-directional references using declarative method(s), and after reading it, I've corrected the domain entities in following way:

@Entity
@Table(name = "EMPLOYERS")
public class Employer implements Serializable {
...
    @JsonManagedReference("employer-employee")
    @OneToMany(mappedBy = "employer", cascade = CascadeType.PERSIST)
    public List getEmployees() {
        return employees;
    }
...
}

@Entity
@Table(name = "EMPLOYEES")
public class Employee implements Serializable {
...
    @JsonManagedReference("employee-benefit")
    @OneToMany(mappedBy = "employee", cascade = CascadeType.PERSIST)
    public List getBenefits() {
        return benefits;
    }

    @JsonBackReference("employer-employee")
    @ManyToOne(optional = false)
    @JoinColumn(name = "EMPLOYER_ID")
    public Employer getEmployer() {
        return employer;
    }
...
}

@Entity
@Table(name = "BENEFITS")
public class Benefit implements Serializable {
...
    @JsonBackReference("employee-benefit")
    @ManyToOne(optional = false)
    @JoinColumn(name = "EMPLOYEE_ID")
    public Employee getEmployee() {
        return employee;
    }
...
}

After performing the above changes, I could finally enjoy the JSON response returned by my code:

[{"id":1, "benefits":[{"name":"Healthy Employees", "id":1, "type":"HEALTH_COVERAGE", "startDate":1104534000000, "endDate":null}, {"name":"Gold Autumn","id":2,"type":"RETIREMENT_PLAN","startDate":1104534000000,"endDate":null},{"name":"Always Secured","id":3,"type":"GROUP_TERM_LIFE","startDate":1104534000000,"endDate":null}],"firstName":"John"},{"id":2,"benefits":[],"firstName":"Mary"},{"id":3,"benefits":[],"firstName":"Eugene"}]

And as usual some links for the dessert:

Sunday, October 10, 2010

Geolocation API - Get Current Position

Look around :) - what are you doing here? how did you get here? and where the heck is "here"? I'll not be able to help you with the first two questions ;) - but the last one can be quickly answered with the Web Browser supporting the W3C Geolocation API.

Let's use following HTML:

<html>
  ...
  <head>
    ...
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" ></script>
    <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false" ></script>
    <script type="text/javascript">
      var map;
      var mapCenter;
      var marker;
            
      function handleError(error) {
        alert('Error: ' + error.message + ' (' + error.code + ')');
      }
            
      function locationFound(position) {        
        // Center map on your location ...        
        mapCenter = new google.maps.LatLng(position.coords.latitude, 
                                           position.coords.longitude);
        map.setCenter(mapCenter);      
        // ... add new marker at map's center ...
        marker = new google.maps.Marker( 
                                { position: mapCenter, 
                                       map: map, title: "Your position" });    
      } 
         
      $(document).ready(
        function() {
          // Create new Google Map using the div having id 'map' ...
          map = new google.maps.Map($('#map')[0], 
                             {      zoom: 14, 
                               mapTypeId: google.maps.MapTypeId.TERRAIN });
          // ... check if your browser supports W3C Geolocation API ...
          if (navigator.geolocation) {
            // ... ask for current position ...
            navigator.geolocation.getCurrentPosition(locationFound, 
                                                     handleError);
          } else {
            alert("Geolocation not available");
          }
      });
    </script>
    ...
  </head>
  <body>
    <div id="map" style="width: 640px; height: 480px;"></div>
  </body>
</html>

I started with Google Chrome and when the above code has loaded in it, it asked first:


With trembling heart I pressed "Allow", and after few moments, I saw the desired position on map:


Don't ask me how they are able to track me down ;) - I can see some possibilities: black magic, IP number tracking, some hidden cameras, or electronic bugs in my apartment ;) - to discard the last possibility, I took my iPhone and went out for the stroll - and guess what ;) - when I opened the above page, iPhone asked me if I want to reveal my current position to the mobile Safari, and when I confirmed, showed me similar map view to the above ...

Some links for the dessert:
Which Devices Support Javascript Geolocation via navigator.geolocation?
How to Use iPhone JavaScript Geolocation
How to use the W3C Geolocation API (Opera)
Using geolocation (Firefox)

Monday, October 4, 2010

Web Application Profiling - Google's Speed Tracer

This is another cartoon post ;) This time about the Speed Tracer, a Google Chrome plugin which allows you to identify the problems related to User Interface of your Web Application.

When you run this tool on some URL you'll see a list of events with their timing:


Each event on the list can be expanded to see its details:


Speed Tracer identifies also potential problems related to the events:


Second tab - named "Network (resources)" presents the network traffic including files required for processing the page (like CSS, or JavaScript)


Selecting specific resource views its details, potential problems are marked with the badges in the resource list on the left, and described in details


Speed Tracer can be also integrated with Spring Insight (see my post Web Application Profiling - Spring Insight)


Interested? Try it yourself :) - I believe it can be helpful for tracking down UI performance problems.

Saturday, October 2, 2010

Web Application Profiling - Spring Insight

Are you writing Web Applications using Spring Framework? Have you ever thought what's going on under the hood? Why your application is responding so slowly? Why the snail outside your window is disappearing in the distance so fast while you still wait for your application response? You should :) and the Spring Insight can be very helpful with this task.

You should start with the tc Server Developer Edition which is in fact well known Apache Tomcat 6.0 on steroids ;). Then you should take a look at the Spring Insight - this tool is installed into tc Server as usual web application, and allows you to take a journey into your own application ;)

I'll put here only few screenshots of Spring Insight in the action, detailed information about installing and using tc Server along with the Spring Insight can be found on the Spring Source web pages.

Let's start ;)


When you click one of the bars on the chart you will see the details of request handling:


As you see on the screen request handling took 316 ms. Trace Detail section holds all methods which were executed during the request handling and all database queries which were performed. 


For each method/database call you can see the details like input parameters, SQL Query executed, or returned value (method only). Selecting request details will show you quick request summary along with request and response headers.

You may wonder why you see the database queries during the view rendering? As you know JPA's lazy loading defers loading the entities from database until they are used for the first time, and it happens during the view rendering. As you see on the above example, there are potential places for improvements (consider dropping lazy usage, use batch reading instead of one by one while fetching lazy entities).

Spring Insight offers you also some statistics about the used application resources - see Browse Resources at the top of page.


Of course you can see the details for each resource:


Summarizing this cartoon post :) - very interesting tool for all of the Spring Framework addicted developers - but in fact not only them - some parts of Spring Insight should be functional also for ordinary Servlet, or application written using some other framework. There is also possibility of writing your own Spring Insight plugins, so don't hesitate ;) use it and extend ;) - it is worth 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)  
 );     

Nice traffic map for Poland

Interesting traffic map for Poland http://mapa.targeo.pl. I'm curious how precise it is, and how often updated ...

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');

Sunday, September 19, 2010

How buggy is your JPA provider ;) ? (episode 1)

My Colleague, Marcin Lewandowski has found the bug reported by me today to the Hibernate team - see: http://opensource.atlassian.com/projects/hibernate/browse/JPA-6

Very shortly - this bug bites ;) when you try to use @ManyToOne mapping with target entity using Single Table inheritance type - in this case Hibernate (3.4.0 GA) generates SQL which doesn't use the discriminator in the where clause, which in turn causes beautiful exception when processing the query results.

Both OpenJPA, and Eclipselink are generating correct SQL queries - for the details see the Hibernate bugtracker.