Blogs

Blogs

Using Spring Data JPA with a Liferay portlet

Service Builder is the way how developers are often encouraged to do DB connectivity with Liferay portlets. It is, though, by no means the one and only way how one can connect to databases from Liferay portlets. In many real world use cases, it is better to use standard JPA for ORM and access a DB that is separate from the DB used by Liferay for its internal stuff (such as user/role management). You portlet might be just one application using the DB and this way you can reuse the same JPA layer in other applications.

Spring Data is probably the best thing you can do to simplify your JPA data access code in general. I wouldn’t build even a trivial JPA based application without it (or its CDI “clone” called DeltaSpike Data). This tutorial gives you an overview of how you can use a modern JPA setup with Liferay portlets. It also introduces a simple method to combine data from your Spring Data repositories and Liferay’s user database - we’ll use email addresses as keys to identify the users in the external database.

In this example we’ll use Vaadin as a UI framework, but the real meat of this article should be easily adaptable to other UI setups as well.

Basis: Spring context and Spring Boot

To startup with any Spring application, we need the core (the IoC container) to be bootstrapped. In this example, we’ll use Spring Boot. Even though we cannot use all of its features in portal environment(namely jar packaging), its convention over configuration features will greatly simplify setting up Spring Data (connecting to the database, connection pooling, setting up persistence context etc).

To get started with Spring Boot, add the following parent to your portlets pom.xml file:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.6.RELEASE</version>
</parent>

The basic dependencies are the same as for typical web apps, but we should exclude tomcat related stuff: org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat

Vaadin Spring doesn’t yet support portlets out of the box, but luckily there is an awesome community that shares their helpers. The popular Vaadin4Spring contains a module that can be used to setup portlet support for Vaadin Spring portlets:

    <dependency>
        <groupId>org.vaadin.spring.addons</groupId>
        <artifactId>vaadin-spring-addon-portlet</artifactId>
        <version>0.0.6</version>
    </dependency>

In addition to the dependency, we’ll need to specify that we use SpringAwareVaadinPortlet instead of raw VaadinLiferayPortlet class in portlet.xml file:

<portlet-class>org.vaadin.spring.portlet.SpringAwareVaadinPortlet</portlet-class>

As a final step, we’ll introduce a similar Spring configuration class as we’d do with a basic (war packaged) Spring Boot app.

@SpringBootApplication
@Import(VaadinConfiguration.class)
@ComponentScan("org.vaadin.springportlet")
@EnableJpaRepositories
public class ApplicationConfiguration extends SpringBootServletInitializer {

    @Configuration
    @PropertySource("classpath:application.properties")
    static class ApplicationProperties {
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(ApplicationConfiguration.class);
    }
}

Finally just annotate your Vaadin UI class with @VaadinPortletUI annotation (from vaadin4spring module) and your object becomes a Spring managed bean: @VaadinPortletUI public class LibraryPortletUI extends UI {

    @Autowired
    ApplicationContext ctx;

    @Override
    protected void init(VaadinRequest request) {
        setContent(new Label("I'm a Spring managed bean in " 
            + ctx.getApplicationName()));
    }

}

Setting up Spring Data

Now that our app has a Spring Boot based Spring context ready, setting up Spring Data and JPA stuff becomes really simple. Just adding @EnableJpaRepositories annotation to your configuration class and adding org.springframework.boot:spring-boot-starter-data-jpa dependency is enough to get started. This brings in both JPA implementation (Hibernate by default) and various Spring Data related JPA tools. Thanks to Spring Boot, the example automatically uses H2 in memory database, just based on the fact that it finds H2 database from the classpath. When going to production, you might want to add another DB driver to your classpath and configure the DB url in application.properties file to point to your own database.

Note, if you are not ready to go with Spring Boot, setting up the basis for Spring Data is slightly more tricky but still perfectly possible. I suggest to follow these sharp instructions which apply for portal environment as well.

In our example we have only one entity, Book, that is declared like this:

@Entity
public class Book extends AbstractEntity {

    @Temporal(TemporalType.TIMESTAMP)
    private Date publishDate;

    @NotNull(message = "Name is required")
    @Size(min = 3, max = 50, message = "name must be longer than 3 and less than 40 characters")
    private String name;

    private String description;

    private String borrowedBy;

    // getters/setters not listed 
}

The superclass, AbstractEntity, just contains basic primary key declarations and often needed proper equals/hashcode implementations.

The basic Spring Data repository for the Book entity can be declared like below. This simple declaration already introduces most methods needed for simple CRUD applications, by inheriting them from the super interfaces.

public interface BookRepository extends JpaRepository<Book, Long> {

}

In a real world use case, you’ll probably want to throw in some domain specific methods, for example, by simply relying on the handy method naming conventions. Spring Data could, for example, implement a method like public List findByEmail(); automatically, without any fiddling with EntityManager or JPQL queries. If you are new to Spring Data, refer to the excellent Spring Data tutorial to get an overview of how powerful a repository pattern can be.

Tie pieces together

At this point we have already made our UI objects Spring managed beans and introduced a repository class to access our entities. Instead of autowiring the repository directly to the UI code, we’ll introduce a service class specifically tailored for our UI code. In this case we’ll make the service UIScoped. This way it will be easier for us to integrate our Spring Data based DB with the user data coming from Liferay’s helper services.

Let’s start with the easy part, injecting in the repository and exposing a couple of trivial methods to our UI:

@Component
@UIScope
public class LibraryPortletUserService {

    @Autowired
    private BookRepository repository;

    public void save(Book entity) {
        repository.save(entity);
    }

    public List<Book> findAll() {
        return repository.findAll();
    }
}

To build integration with Liferay’s user data and our own database, we’ll need to get references to User and Company object. A simple way to find them is to use VaadinLiferayRequest that provides a current PortletRequest by a static helper method. The @PostConstruct method of the UIScoped bean is executed during an http request, so we can gain references to PortletRequest safely via helpers found from the VaadinLiferayRequest class.

private User currentUser;

private Company company;

@PostConstruct
public void init() {
    initUserAndCompany();
}

private void initUserAndCompany() {
    try {
        currentUser = PortalUtil.getUser(VaadinLiferayRequest.getCurrentPortletRequest());
        company = PortalUtil.getCompany(VaadinLiferayRequest.getCurrentPortletRequest());
    } catch (PortalException e1) {
        e1.printStackTrace();
    } catch (SystemException e1) {
        e1.printStackTrace();
    }
}

Now making that loose connection between Spring Data managed DB and portal user database becomes trivial, and most essentially, it becomes transparent to the UI code. An example of the borrowBook business method:

/**
 * Sets the email field of the Book entity to current Liferay users
 * main email and persists changes to DB.
 * 
 * @param b the book to be set borrowed by current user
 */
public void borrowBook(Book b) {
    b.setBorrowedBy(getCurrentUser().getEmailAddress());
    repository.save(b);
}

As the final step, we’ll simply use the @Autowired annotation to get a reference to the service in our UI code. E.g. listing entities in our UI becomes as simple as this:

@Autowired
LibraryPortletUserService service;

private MTable<Book> bookListing;

@Override
protected void init(VaadinRequest request) {
    bookListing.setBeans(service.findAll());
}

Trying it yourself

The full source code I used to write this introduction is available in github. The easiest way to try it is naturally just executing mvn install and deploying the war file to your Liferay portal. More than that, I suggest to install the full Liferay SDK, with proper Maven profiles, and play with your custom version locally.

Check out the example portlet from Github

Matti, dude, I've been waiting for this for a long time. Serisouly, thanks for posting this! Spring is truly awesome!
Matti, it is a great post, but I have a question... Is it possible to implement the same spring configuration without spring boot? Thanks emoticon
Yes, that is how I actually started my example app in the first place. That just needs bit more effort to configure Spring Data JPA properly.

I think it might be still available in the git history... Yes, here:

https://github.com/mstahv/spring-data-vaadin-portlet/tree/1a31d623f418ae4b0e72a8beaa2b315d83d3c597

That version contains some SNAPSHOT versions, but should be really easy to move to release versions.
Thanks! I have done this one, but let me tell you:

I am working on an activiti based portlets application, so I have to load a process engine and other beans from an applicationContextx file, but I dont know how to do that with a Spring boot project, I just want to use spring applicationFile configuration inside a portlet. I am trying to implement spring mvc, but also I am using vaadin, Iam a bit lost here :/. Thanks for your reply emoticon
Hello Matti,

Could you on below post..
https://web.liferay.com/community/forums/-/message_boards/message/97824764

Thanks
Hi All,

Take into account that when deploying vaadin-spring portlets in Liferay 7 you need to package the needed javax classes for PostConstruct and Predestory within your war file, since the osgi runtime does not load the clases by default. I did this by adding the following maven dependency:

<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>

Regards,

Kim

Hi Kim! 

Is it possible that you could share the basics of your project with us?

I've alerady spent hufe amount of time trying to configure LR7 with vaadin and spring without any succesful results.

Looking forward to hearing from you.

 

Cheers!