How do I show Spring transaction in log / console?

When you use the Spring framework @Transactional annotation in your service layer you might want to see what is happening in your code related to database transaction. You want to see when a transaction is started, when it is committed or rollbacked.

To activate the log for transactional message you can add the following configurations in your application properties file. For example when using the JpaTransactionManager you can set the log level to DEBUG.

logging.level.root=INFO

logging.level.org.springframework.orm.jpa=DEBUG
logging.level.org.springframework.transaction=DEBUG

Running the spring boot application with these configuration, the JpaTransactionManager will write something line these on your log file or console:

2023-03-29T23:06:52.194+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Creating new transaction with name [org.kodejava.webapp.accounting.service.impl.CalculationServiceImpl.recalculate]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2023-03-29T23:06:52.194+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Opened new EntityManager [SessionImpl(974784570<open>)] for JPA transaction
2023-03-29T23:06:52.195+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@4150907e]
...
...
2023-03-29T23:06:52.195+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(974784570<open>)] for JPA transaction
2023-03-29T23:06:52.195+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Participating in existing transaction
...
...
2023-03-29T23:06:52.237+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Initiating transaction commit
2023-03-29T23:06:52.237+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Committing JPA transaction on EntityManager [SessionImpl(974784570<open>)]
2023-03-29T23:06:52.237+08:00 DEBUG 54056 --- [nio-9090-exec-3] o.s.orm.jpa.JpaTransactionManager        : Closing JPA EntityManager [SessionImpl(974784570<open>)] after transaction

How do I build simple search page using ZK and Spring Boot?

In this example we are going to build a simple search page using ZK framework and Spring Boot. We are going to use the latest available version of Spring Boot (3.0.0) and ZK Framework (9.6.0.2). So without taking more time let’s start by creating a new spring boot project with the following pom.xml. You can create the initial project using your IDE or spring initializr.

Create a Spring Boot project and add the following dependencies:

  • zkspringboot-starter
  • zkplus
  • spring-boot-devtools
  • spring-boot-starter-data-jpa
  • mysql-connector-j
  • lombok

The pom.xml File

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>org.kodejava</groupId>
    <artifactId>kodejava-zk-search</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>kodejava-zk-search</name>
    <description>kodejava-zk-search</description>

    <properties>
        <java.version>17</java.version>
        <zkspringboot.version>3.0.0</zkspringboot.version>
        <zk.version.jakarta>9.6.0.2-jakarta</zk.version.jakarta>
        <zk.version>9.6.0.2</zk.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.zkoss.zkspringboot</groupId>
            <artifactId>zkspringboot-starter</artifactId>
            <type>pom</type>
            <version>${zkspringboot.version}</version>
        </dependency>
        <dependency>
            <groupId>org.zkoss.zk</groupId>
            <artifactId>zkplus</artifactId>
            <version>${zk.version.jakarta}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>ZK CE</id>
            <name>ZK CE Repository</name>
            <url>https://mavensync.zkoss.org/maven2</url>
        </repository>
        <repository>
            <id>ZK EVAL</id>
            <name>ZK Evaluation Repository</name>
            <url>https://mavensync.zkoss.org/eval</url>
        </repository>
    </repositories>

</project>

application.properties File

This properties file configure ZK application homepage and the prefix where the zul files are located. We also configure the datasource to our application database.

zk.homepage=label
zk.zul-view-resolver-prefix=/zul
zk.resource-uri=/zkres

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/musicdb
spring.datasource.username=root
spring.datasource.password=

spring.jpa.properties.hibernate.hbm2ddl.auto=create

Label.java Entity Definition

An entity that represent out record label with just two property of id and name. Getters and setters are generated by Lombok library, it also generated to equals() and hashcode() method, and also the toString() method.

package org.kodejava.zk.entity;

import jakarta.persistence.*;
import lombok.Data;

@Data
@Entity
public class Label {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}

The LabelRepository.java definition.

Create the LabelRepository which extends the JpaRepository and JpaSpecificationExecutor interfaces.

package org.kodejava.zk.repository;

import org.kodejava.zk.entity.Label;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

@Repository
public interface LabelRepository extends JpaRepository<Label, Long>, JpaSpecificationExecutor<Label> {
}

AbstractSearchController.java a base search controller.

A base class that we can use to implements all the search page in an application. Basically it provides the method to search our application data. It defines a couple of abstract method that need to be implemented by the search controller classes such as what repository to use and the specification for searching the data. We can also define the default sort column and the direction of the data sorting.

package org.kodejava.zk.controller;

import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.VariableResolver;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.Listbox;

@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
public abstract class AbstractSearchController<T> extends SelectorComposer<Component> {
    @Wire
    protected Listbox listbox;

    public abstract JpaSpecificationExecutor<T> getRepository();

    public abstract Specification<T> getSpecification();

    public abstract String getCacheKey();

    protected String getDefaultSortColumn() {
        return "id";
    }

    protected Sort.Direction getDefaultSortDirection() {
        return Sort.Direction.ASC;
    }

    protected boolean getMultiple() {
        return false;
    }

    @Override
    public void doAfterCompose(Component comp) throws Exception {
        super.doAfterCompose(comp);
        search();
    }

    @Listen("onClick=#searchButton")
    public void search() {
        listbox.setVisible(true);
        SearchListModel<T> model = new SearchListModel<>(getRepository(), getSpecification(), getCacheKey());
        model.setMultiple(getMultiple());
        model.setDefaultSortColumn(getDefaultSortColumn());
        model.setDefaultSortDirection(getDefaultSortDirection());
        listbox.setModel(model);
        listbox.setActivePage(0);
    }

    @Listen("onOK=#searchForm")
    public void onEnterPressed(Event event) {
        search();
    }

    public int getPageSize() {
        return SearchListModel.PAGE_SIZE;
    }
}

SearchListModel.java

An implementation of ListModel, this class will query the database using the provided repository and specification. It read data page-by-page and cache it so when we navigating the Listbox page it doesn’t read the data that have already been cached.

package org.kodejava.zk.controller;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.zkoss.zk.ui.Execution;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zul.FieldComparator;
import org.zkoss.zul.ListModelList;

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

public class SearchListModel<T> extends ListModelList<T> {
    public static final int PAGE_SIZE = 5;
    private final JpaSpecificationExecutor<T> repository;
    private final String cacheKey;
    private long totalElements;

    private Comparator<T> comparator;
    private boolean ascending = false;
    private final Specification<T> specification;
    private Sort.Direction defaultSortDirection = Sort.Direction.ASC;
    private String defaultSortColumn = "id";

    public SearchListModel(JpaSpecificationExecutor<T> repository, Specification<T> specification, String cacheKey) {
        this.repository = repository;
        this.specification = specification;
        this.cacheKey = cacheKey;
        this.totalElements = repository.count(specification);
    }

    @Override
    public T getElementAt(int index) {
        Map<Integer, T> cache = getCache();

        T target = cache.get(index);
        if (target == null) {
            Sort sort = Sort.by(getDefaultSortDirection(), getDefaultSortColumn());
            if (comparator != null) {
                FieldComparator fieldComparator = (FieldComparator) comparator;
                String orderBy = fieldComparator.getRawOrderBy();
                sort = Sort.by(ascending ? Sort.Direction.ASC : Sort.Direction.DESC, orderBy);
            }
            Page<T> pageResult = repository.findAll(specification, PageRequest.of(getPage(index), PAGE_SIZE, sort));
            totalElements = pageResult.getTotalElements();
            int indexKey = index;
            for (T t : pageResult.toList()) {
                cache.put(indexKey, t);
                indexKey++;
            }
        } else {
            return target;
        }

        target = cache.get(index);
        if (target == null) {
            throw new RuntimeException("element at " + index + " cannot be found in the database.");
        } else {
            return target;
        }
    }

    @Override
    public int getSize() {
        return (int) totalElements;
    }

    @Override
    public void sort(Comparator<T> comparator, boolean ascending) {
        super.sort(comparator, ascending);
        this.comparator = comparator;
        this.ascending = ascending;
    }

    @SuppressWarnings("unchecked")
    private Map<Integer, T> getCache() {
        Execution execution = Executions.getCurrent();
        Map<Integer, T> cache = (Map<Integer, T>) execution.getAttribute(cacheKey);
        if (cache == null) {
            cache = new HashMap<>();
            execution.setAttribute(cacheKey, cache);
        }
        return cache;
    }

    private int getPage(int index) {
        if (index != 0) {
            return index / PAGE_SIZE;
        }
        return index;
    }

    public Sort.Direction getDefaultSortDirection() {
        return defaultSortDirection;
    }

    public void setDefaultSortDirection(Sort.Direction defaultSortDirection) {
        this.defaultSortDirection = defaultSortDirection;
    }

    public String getDefaultSortColumn() {
        return defaultSortColumn;
    }

    public void setDefaultSortColumn(String defaultSortColumn) {
        this.defaultSortColumn = defaultSortColumn;
    }
}

LabelSearchController.java

Our label search page controller which extends from AbstractSearchController class. We provide the LabelRepository and the Specification to filter the data.

package org.kodejava.zk.controller;

import jakarta.persistence.criteria.Predicate;
import org.kodejava.zk.entity.Label;
import org.kodejava.zk.repository.LabelRepository;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.zkoss.zk.ui.select.annotation.VariableResolver;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import org.zkoss.zul.Textbox;

import java.util.ArrayList;
import java.util.List;

@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
public class LabelSearchController extends AbstractSearchController<Label> {
    @WireVariable
    private LabelRepository labelRepository;

    @Wire
    private Textbox labelNameTextbox;

    @Override
    public JpaSpecificationExecutor<Label> getRepository() {
        return labelRepository;
    }

    @Override
    public Specification<Label> getSpecification() {
        return (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            String labelName = labelNameTextbox.getValue();
            if (!labelName.isBlank()) {
                predicates.add(criteriaBuilder.like(root.get("name"), "%" + labelName + "%"));
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
    }

    @Override
    public String getCacheKey() {
        return "LABEL_CACHE_KEY";
    }
}

label-search.zul – Label Search Page in ZUL

The search page with a label name Textfield to search by label name. The Listbox will display the data with pagination.

<zk>
    <window id="labelSearchWin" zclass="none" border="none" visible="true"
            apply="org.kodejava.zk.controller.LabelSearchController" width="100%">

        <grid id="searchForm">
            <columns>
                <column width="200px"/>
                <column/>
            </columns>
            <rows>
                <row>
                    <label value="Label Name"/>
                    <textbox id="labelNameTextbox" width="450px" maxlength="50"/>
                </row>
            </rows>
        </grid>

        <separator/>

        <hlayout>
            <button id="searchButton" label="Search"/>
        </hlayout>

        <separator/>

        <vlayout>
            <listbox id="listbox" mold="paging" pageSize="${labelSearchWin$composer.pageSize}" visible="false">
                <listhead>
                    <listheader label="No" hflex="min"/>
                    <listheader label="Label Name" sort="auto(name)"/>
                    <listheader label="Action" hflex="min"/>
                </listhead>
                <template name="model">
                    <listitem>
                        <listcell label="${forEachStatus.index + 1}" hflex="min"/>
                        <listcell label="${each.name}"/>
                        <listcell hflex="min">
                            <hlayout>
                                <button label="Edit" forward="onClick=listbox.onEdit(${each})" tooltiptext="Edit Data"/>
                            </hlayout>
                        </listcell>
                    </listitem>
                </template>
            </listbox>
        </vlayout>
    </window>
</zk>

Running the application and access it at localhost:8080 will give you a screen like the screenshot at the beginning of this post.

The complete source code can be found in the following GitHub repository kodejava-zk-search.

How do I combine filter and projection operation in Spring EL?

Using Spring Expression Language (SpEL) we can filter a collection based on some criteria. We can also create a projection of a collection by collecting only a particular property from the collection objects.

Now you know that you have two good features of SpEL that are really powerful to use when working with collection objects manipulation. But you are wondering how to combine both of these filters and projections in one expression. Can you do this in Spring EL? The answer is yes! You can combine them both in one expression. Let’s see an example below.

We are going to use the same configuration used in the previous example:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <util:list id="books">
        <bean class="org.kodejava.spring.core.el.Book" p:title="Essential C# 4.0" p:author="Michaelis" p:pages="450" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="User Stories Applied" p:author="Mike Cohen"
              p:pages="268" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="Learning Android" p:author="Marco Gargenta"
              p:pages="245" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="The Ruby Programming Language"
              p:author="David Flanagan & Yukihiro Matsumoto" p:pages="250" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="Einstein" p:author="Walter Isaacson" p:pages="1000" />
    </util:list>

    <bean id="library" class="org.kodejava.spring.core.el.Library">
        <property name="bookTitles" value="#{books.?[pages gt 250].![title]}" />
    </bean>

</beans>

In the configuration above, when we define the library bean we set its bookTitles property using the filtering and projection operator. First we take only books that have more than 250 pages, and then we create the projection that contains only the book title. So this expression give us all the book’s title of a book that has more than 250 pages.

To make the example complete here again the definition of the Book and the Library class.

package org.kodejava.spring.core.el;

public class Book {
    private Long id;
    private String title;
    private String author;
    private String type;
    private int pages;

    // Getters & Setters
}
package org.kodejava.spring.core.el;

import java.util.List;

public class Library {
    private List<Book> books;
    private List<String> bookTitles;

    // Getters & Setters
}

The main class the run the configuration file:

package org.kodejava.spring.core.el;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpELFilterProjectionExample {
    public static void main(String[] args) {
        try (ClassPathXmlApplicationContext context =
                     new ClassPathXmlApplicationContext("spel-filter-projection.xml")) {

            Library library = context.getBean("library", Library.class);

            for (String title : library.getBookTitles()) {
                System.out.println("title = " + title);
            }
        }
    }
}

The result of the code snippet:

title = Essential C# 4.0
title = User Stories Applied
title = Einstein

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>

Maven Central Maven Central Maven Central

How do I create a projection of a collection using Spring EL?

In this example you will learn how to create a projection of a collection object. Using projection we can create a new collection with only a specific property from the original collection.

As an example, instead of returning a collection of Book objects we would like only to have the titles of the books. To do this we can use the Spring EL projection operator. The symbol use for this operator is ![].

Let’s begin by creating the Spring configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <util:list id="books">
        <bean class="org.kodejava.spring.core.el.Book" p:title="Essential C# 4.0" p:author="Michaelis" p:pages="450" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="User Stories Applied" p:author="Mike Cohen"
              p:pages="268" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="Learning Android" p:author="Marco Gargenta"
              p:pages="245" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="The Ruby Programming Language"
              p:author="David Flanagan & Yukihiro Matsumoto" p:pages="250" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="Einstein" p:author="Walter Isaacson" p:pages="1000" />
    </util:list>

    <bean id="library" class="org.kodejava.spring.core.el.Library">
        <property name="bookTitles" value="#{books.![title]}" />
    </bean>

</beans>

Here are the definition of the Book and Library class. The getters and setters methods were removed for simplicity of the snippet.

package org.kodejava.spring.core.el;

public class Book {
    private Long id;
    private String title;
    private String author;
    private String type;
    private int pages;

    // Getters & Setters
}
package org.kodejava.spring.core.el;

import java.util.List;

public class Library {
    private List<Book> books;
    private List<String> bookTitles;

    // Getters & Setters
}

Now, let’s talk about the Spring configuration above. The configuration was start by creating a collection of Books using the <util:elements>. The part that use the projection operator is this part of the configuration:

<bean id="library" class="org.kodejava.spring.core.el.Library">
    <property name="bookTitles" value="#{books.![title]}"/>
</bean>

The bean element above create a library bean of type org.kodejava.spring.core.el.Library. We assign the bean’s bookTitles property with values that are a projection of the books collection where we take only the title of the books. ![projectionExpression] is the syntax of the projection operator.

The code snippet below will demonstrate and run our configuration file and print out the result:

package org.kodejava.spring.core.el;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpELProjectionExample {
    public static void main(String[] args) {
        try (ClassPathXmlApplicationContext context =
                     new ClassPathXmlApplicationContext("spel-projection.xml")) {

            Library library = context.getBean("library", Library.class);

            for (String title : library.getBookTitles()) {
                System.out.println("title = " + title);
            }
        }
    }
}

And here are the result:

title = Essential C# 4.0
title = User Stories Applied
title = Learning Android
title = The Ruby Programming Language
title = Einstein

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>

Maven Central Maven Central Maven Central

How do I filter collection members based on some criteria using Spring EL?

In previous examples you have seen that we use the square-braces [] operator to select items from collection. In this post you will learn how to filter members of a collection with a certain criteria using the Spring EL. To do this Spring EL give you another special operator, the filter operator which can be typed like .?[], you can define the filter criteria inside the braces.

Beside the filter operator .?[] there are also operator that can select the first and the last matching items from collection. You can use the .^[] for selecting the first match and the .$[] operator to select the last match items from collection respectively.

As an example we will demonstrate here that we want to find a collection of books that have pages more than 250 and assign it to Library bean’s books properties. So let’s begin by defining a class called Book and Library which will have the following properties:

package org.kodejava.spring.core.el;

public class Book {
    private Long id;
    private String title;
    private String author;
    private String type;
    private int pages;

    // Getters & Setters

    @Override
    public String toString() {
        return "Book{" +
                "title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", pages=" + pages +
                '}';
    }
}
package org.kodejava.spring.core.el;

import java.util.List;

public class Library {
    private List<Book> books;

    public List<Book> getBooks() {
        return books;
    }

    public void setBooks(List<Book> books) {
        this.books = books;
    }
}

After creating the Book and the Library class let’s now create the Spring configuration file for our demo. We will create a file and call it as spel-filter-collection.xml with the following lines of configuration in it.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <util:list id="books">
        <bean class="org.kodejava.spring.core.el.Book" p:title="Essential C# 4.0" p:author="Michaelis" p:pages="450" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="User Stories Applied" p:author="Mike Cohen"
              p:pages="268" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="Learning Android" p:author="Marco Gargenta"
              p:pages="245" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="The Ruby Programming Language"
              p:author="David Flanagan & Yukihiro Matsumoto" p:pages="250" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="Einstein" p:author="Walter Isaacson" p:pages="1000" />
    </util:list>

    <bean id="lib1" class="org.kodejava.spring.core.el.Library">
        <property name="books" value="#{books.?[pages gt 250]}" />
    </bean>

    <bean id="lib2" class="org.kodejava.spring.core.el.Library">
        <property name="books" value="#{books.^[pages gt 250]}" />
    </bean>

    <bean id="lib3" class="org.kodejava.spring.core.el.Library">
        <property name="books" value="#{books.$[pages gt 250]}" />
    </bean>

</beans>

The first thing that you can see in the configuration above is the <util-list> where we create a list of Book beans. Next we have three beans definition of the type Library where the books property was assigned with a collection of beans selected from the books list.

The lib1 will contains books that have pages more than 250. The lib2 will contains the first book found to have more than 250 pages while the lib3 will contains the last book found to have more than 250 pages.

Below is the code that will run our configuration file and the example output it will produce.

package org.kodejava.spring.core.el;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpELFilterCollection {
    public static void main(String[] args) {
        try (ClassPathXmlApplicationContext context =
                     new ClassPathXmlApplicationContext("spel-filter-collection.xml")) {

            System.out.println("Library 1");
            Library lib1 = (Library) context.getBean("lib1");
            for (Book book : lib1.getBooks()) {
                System.out.println("    Book = " + book);
            }

            System.out.println("Library 2");
            Library lib2 = (Library) context.getBean("lib2");
            for (Book book : lib2.getBooks()) {
                System.out.println("    Book = " + book);
            }

            System.out.println("Library 3");
            Library lib3 = (Library) context.getBean("lib3");
            for (Book book : lib3.getBooks()) {
                System.out.println("    Book = " + book);
            }
        }
    }
}
Library 1
    Book = Book{title='Essential C# 4.0', author='Michaelis', pages=450}
    Book = Book{title='User Stories Applied', author='Mike Cohen', pages=268}
    Book = Book{title='Einstein', author='Walter Isaacson', pages=1000}
Library 2
    Book = Book{title='Essential C# 4.0', author='Michaelis', pages=450}
Library 3
    Book = Book{title='Einstein', author='Walter Isaacson', pages=1000}

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>

Maven Central Maven Central Maven Central

How do I read system environment variables in Spring EL?

Previously you have seen that we can load properties file and read a value from it in this example: How do I read a value from properties file using Spring EL?. In this example you will learn how to read a special properties available to Spring EL. These properties include the systemEnvironment and systemProperties.

The systemEnvironment property contains all the environment variables on the machine where the program is running. Meanwhile, the systemProperties contains all the properties that we set in Java when the application started, using the -D argument. Let’s see how to access both of these properties in the following Spring configuration file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 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.xsd">

    <bean id="program1" class="org.kodejava.spring.core.el.Program">
        <property name="logPath" value="#{systemProperties['APP.LOG_PATH']}" />
    </bean>

    <bean id="program2" class="org.kodejava.spring.core.el.Program">
        <property name="logPath" value="#{systemEnvironment['TEMP']}" />
    </bean>

</beans>

In the configuration above we have two beans of Program. We set the logPath properties using a different property source. In the program1 bean we use systemProperties['APP.LOG_PATH']. Using this method the value will be pass to our program using the -DAPP.LOG_PATH=F:\Temp when we are executing the program. While the program2 bean’s logPath is read from TEMP directory property available through the systemEnvironment variables.

To make the Spring configuration works you’ll need the Program class. So here is the class definition.

package org.kodejava.spring.core.el;

public class Program {
    private String logPath;

    public Program() {
    }

    public String getLogPath() {
        return logPath;
    }

    public void setLogPath(String logPath) {
        this.logPath = logPath;
    }
}

Finally, let’s create a simple class to execute the Spring configuration file above and see the result of the code.

package org.kodejava.spring.core.el;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpELEnvironment {
    public static void main(String[] args) {
        try (ClassPathXmlApplicationContext context =
                     new ClassPathXmlApplicationContext("spel-environment.xml")) {

            Program program1 = (Program) context.getBean("program1");
            System.out.println("program.getLogPath() = " + program1.getLogPath());

            Program program2 = (Program) context.getBean("program2");
            System.out.println("program.getLogPath() = " + program2.getLogPath());
        }
    }
}

The code will print the following result:

program.getLogPath() = F:\Temp
program.getLogPath() = C:\Users\wsaryada\AppData\Local\Temp

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>

Maven Central Maven Central Maven Central

How do I read a value from properties file using Spring EL?

In the previous two examples you have seen how to access member of a collection and access a map element using the square-braces [] operator in Spring EL. In this example you will see how to use the [] operator to read a value from a properties file or java.util.Properties.

Let’s say we have a database properties file called database.properties with the following entries in it:

jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/kodejava
jdbc.username=root
jdbc.password=secret

First, let’s create the spring configuration file. In this configuration we will use the <util:properties> to load the properties file into Spring. And then we will use Spring EL to access the value of these properties and assign it to some bean’s properties.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <util:properties id="database" location="classpath:database.properties" />

    <bean id="dataSource" class="org.kodejava.spring.core.el.MyDataSource">
        <property name="driverClassName" value="#{database['jdbc.driverClassName']}" />
        <property name="url" value="#{database['jdbc.url']}" />
        <property name="username" value="#{database['jdbc.username']}" />
        <property name="password" value="#{database['jdbc.password']}" />
    </bean>
</beans>

To read a value from properties file what you do is the same as how we access an element of a map object. We pass the name of the properties as the key in the Spring EL.

<property name="driverClassName" value="#{database['jdbc.driverClassName']}"/>

The MyDataSource class is an imaginary data source object. It has some properties such as the driverClassName, url, username and password. It’s a common parameter you use to connect to a database using a JDBC driver. For simplicity the getters and setters we removed from the class.

package org.kodejava.spring.core.el;

public class MyDataSource {
    private String driverClassName;
    private String url;
    private String username;
    private String password;

    // Getters & Setters
}

As always, to run the Spring configuration above we will need to create a main class that load and execute the application context. This class will obtain the dataSource bean from the application context and print out its properties whose values are read from a properties file called database.properties.

package org.kodejava.spring.core.el;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpELPropertiesExample {
    public static void main(String[] args) {
        try (ClassPathXmlApplicationContext context =
                     new ClassPathXmlApplicationContext("spel-properties.xml")) {

            MyDataSource dataSource = (MyDataSource) context.getBean("dataSource");
            System.out.println("driverClassName = " + dataSource.getDriverClassName());
            System.out.println("url             = " + dataSource.getUrl());
            System.out.println("username        = " + dataSource.getUsername());
            System.out.println("password        = " + dataSource.getPassword());
        }
    }
}

Here are the result you get when running the code snippet:

driverClassName = com.mysql.cj.jdbc.Driver
url             = jdbc:mysql://localhost/kodejava
username        = root
password        = secret

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>

Maven Central Maven Central Maven Central

How do I access Map element using Spring EL?

In the previous example, How do I access collections members using Spring EL?, you have seen how to access a member of a collection using Spring EL square-braces [] operator. In this post you will learn how to use the same operator to access an element of a Map object.

For demonstration, we will use the same Book class in the previous example to create the bean. The class without the corresponding getters and setters is as follows:

package org.kodejava.spring.core.el;

public class Book {
    private Long id;
    private String title;
    private String author;
    private String type;

    // Getters & Setters
}

Next, let’s create the spring configuration file. In this configuration file we create a map using the <util:map> with the map id of books and add some key-value pair entries in the map.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <util:map id="books">
        <entry key="0-201-61622-X" value="The Pragmatic Programmer" />
        <entry key="978-1-934356-56-2" value="Hello, Android" />
        <entry key="978-1-933988-69-6" value="Secret of The JavaScript Ninja" />
        <entry key="978-1-449-37017-6" value="Java EE 7 Essentials" />
        <entry key="9781935182962" value="Spring Roo in Action" />
    </util:map>

    <bean id="book1" class="org.kodejava.spring.core.el.Book" p:title="#{books['9781935182962']}" />
    <bean id="book2" class="org.kodejava.spring.core.el.Book" p:title="#{books['978-1-933988-69-6']}" />

</beans>

After defining the map, you can see how we access an element of the map. We use the square-braces operator [], we use the same operator as we are accessing a collection member. But instead of passing the index to the operator we pass the key of the map element that we are going to read.

<bean id="book2" class="org.kodejava.spring.core.el.Book" p:title="#{books['978-1-933988-69-6']}"/>

Finally, to run the configuration you’ll need to create the following class:

package org.kodejava.spring.core.el;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpELMapExample {
    public static void main(String[] args) {
        try (ClassPathXmlApplicationContext context =
                     new ClassPathXmlApplicationContext("spel-map.xml")) {

            Book book1 = (Book) context.getBean("book1");
            Book book2 = (Book) context.getBean("book2");

            System.out.println("book1.getTitle() = " + book1.getTitle());
            System.out.println("book2.getTitle() = " + book2.getTitle());
        }
    }
}

And example of output produced by this code can be seen below:

book1.getTitle() = Spring Roo in Action
book2.getTitle() = Secret of The JavaScript Ninja

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>

Maven Central Maven Central Maven Central

How do I access collections members using Spring EL?

In this post you will see another powerful examples of Spring EL. We are going to demonstrate how to use Spring EL to access collection members.

By using Spring EL you can select a single reference member of a collection. You can also select members of collection based on the values of their properties. Another thing you can do is extract properties out of the collection members to create another collection object.

To demonstrate this we are going to create a simple bean / pojo as our collection object. We will create a Book class with some properties (id, title, author).

package org.kodejava.spring.core.el;

public class Book {
    private Long id;
    private String title;
    private String author;
    private String type;

    // Getters & Setters
}

Next, we need to create the spring configuration file. In this configuration we will create a collection of books using the <util:list> element. And we also create a bean with its properties is obtained from one of the collection objects.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <util:list id="books">
        <bean class="org.kodejava.spring.core.el.Book" p:title="Essential C# 4.0" p:author="Michaelis" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="User Stories Applied" p:author="Mike Cohen" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="Learning Android" p:author="Marco Gargenta" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="The Ruby Programming Language"
              p:author="David Flanagan & Yukihiro Matsumoto" />
        <bean class="org.kodejava.spring.core.el.Book" p:title="Einstein" p:author="Walter Isaacson" />
    </util:list>

    <bean id="book" class="org.kodejava.spring.core.el.Book">
        <property name="title" value="#{books[3].title}" />
        <property name="author" value="#{books[3].author}" />
    </bean>

</beans>

In the configuration above you have seen how we set the title and author of the book bean. We use the square-braces operator ([]) to access collection’s member by their index. It’s look like this:

<property name="title" value="#{books[3].title}"/>
<property name="author" value="#{books[3].author}"/>

Which can be read as: please give me the collection object at index number 3 and take the value of its title and author to be assigned to the book bean. And as you might already know that a collection object in Java is always zero-based index. So this will give us the book with title “The Ruby Programming Language”.

And finally let’s create an example class that run our spring configuration above. It’s simply load the spell-collection.xml configuration we create above. Get a bean from the loaded ApplicationContext and print out its properties, title and author properties.

package org.kodejava.spring.core.el;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpELCollectionExample {
    public static void main(String[] args) {
        try (ClassPathXmlApplicationContext context =
                     new ClassPathXmlApplicationContext("spel-collection.xml")) {

            Book book = (Book) context.getBean("book");
            System.out.println("book.getTitle() = " + book.getTitle());
            System.out.println("book.getAuthor() = " + book.getAuthor());
        }
    }
}

Executing the code above will give you the following result:

book.getTitle() = The Ruby Programming Language
book.getAuthor() = David Flanagan & Yukihiro Matsumoto

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>

Maven Central Maven Central Maven Central

How do I do pattern matching using regular expression in Spring EL?

In this Spring Expression Language example we are going to learn how to use regular expression or regex to check if a text matches a certain pattern. Spring EL support regular expression using the matches operator.

The matches operator will check if the value has a pattern defined by the regex string, and it returns the evaluation result as a boolean value true if the text matches the regex or false if otherwise.

For example, we can use the matches operator to check if the given email address is a valid email address. As can be seen in the following example:

<property name="emailValid" 
          value="#{user.email matches '^[_A-Za-z0-9-]+(.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(.[A-Za-z0-9]+)*(.[A-Za-z]{2,})$'}"/>

This configuration will evaluate the user.email property to check if the email pattern matches with the given regular expression. If matches then the emailValid property will be set to true otherwise it will be false.

Let’s see the complete example. Here are the spring configuration file, the User bean and a simple class for running the configuration file.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 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.xsd">

    <bean id="user" class="org.kodejava.spring.core.el.User">
        <constructor-arg name="email" value="kodejava@gmail.com" />
        <property name="emailValid"
                  value="#{user.email matches '^[_A-Za-z0-9-]+(.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(.[A-Za-z0-9]+)*(.[A-Za-z]{2,})$'}" />
    </bean>

    <bean id="user2" class="org.kodejava.spring.core.el.User">
        <constructor-arg name="email" value="kodejava.at.gmail.dot.com" />
        <property name="emailValid"
                  value="#{user2.email matches '^[_A-Za-z0-9-]+(.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(.[A-Za-z0-9]+)*(.[A-Za-z]{2,})$'}" />
    </bean>

</beans>

The User bean is a simple pojo with two properties, a string email property and a boolean validEmail property.

package org.kodejava.spring.core.el;

public class User {
    private String email;
    private boolean emailValid;

    public User() {
    }

    public User(String email) {
        this.email = email;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public boolean isEmailValid() {
        return emailValid;
    }

    public void setEmailValid(boolean emailValid) {
        this.emailValid = emailValid;
    }
}

And finally the application class.

package org.kodejava.spring.core.el;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpELRegexExample {
    public static void main(String[] args) {
        try (ClassPathXmlApplicationContext context =
                     new ClassPathXmlApplicationContext("spel-regex.xml")) {

            User user = (User) context.getBean("user");
            System.out.println("user.getEmail()     = " + user.getEmail());
            System.out.println("user.isEmailValid() = " + user.isEmailValid());

            User user2 = (User) context.getBean("user2");
            System.out.println("user.getEmail()     = " + user2.getEmail());
            System.out.println("user.isEmailValid() = " + user2.isEmailValid());
        }
    }
}

When we run the code we will obtain the following result:

user.getEmail()     = kodejava@gmail.com
user.isEmailValid() = true
user.getEmail()     = kodejava.at.gmail.dot.com
user.isEmailValid() = false

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>

Maven Central Maven Central Maven Central