Spring JdbcBeanDefinitionReader Example
This article is about the Spring JdbcBeanDefinitionReader
. When would you use this class? When you want to dynamically add externally defined bean definitions in Spring. The Spring JdbcBeanDefinitionReader
is a bean definition reader that reads values from a database table and expects columns for bean name, property name and value as String
. The formats for each are identical to the properties format recognized by PropertiesBeanDefinitionReader
.
1. Tools
2. Assumptions
This article assumes that you know your way around Eclipse. You are familiar with Maven. And you are familiar with Spring Boot. Basically, you have done some coding. This project has been created using Eclipse Mars so all instructions are based on this IDE.
3. Project Setup
To start, we create our project. This can be done by going to File -> New -> Maven Project and fill up what is required.
Our pom.xml
should look like the one below:
pom.xml
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.javacodegeeks.example</groupId> <artifactId>spring-jdbcbeandefinitionreader</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
As shown above, our project has 2 dependencies. We are using spring-boot-starter-jdbc
which means that we can use all the Spring modules included in it. For example, the Spring Core and Spring JDBC modules will be available for us to use plus many more. The next dependency is the H2 Database Engine. We will be utilizing H2’s in-memory database for this example.
The Spring Boot Maven plugin enables us to package the project as an executable jar.
4. POJOs
Below are the plain old Java objects used in this example. In this example, you’ll be using using coffee beans as your Java objects. The author has a weird imagination or no imagination at all :) Arabica
and Barako
are of type Seed
. And CoffeeBean
is a concrete class.
Seed.java
package com.javacodegeeks.example; public interface Seed { public String getWeight(); }
Arabica.java
package com.javacodegeeks.example; public class Arabica implements Seed { private String weight; public Arabica() {} public Arabica(String weight) { setWeight(weight); } public void setWeight(String weight) { this.weight = weight; } @Override public String getWeight() { return this.weight; } @Override public String toString() { return "Arabica [weight=" + weight + "]"; } }
Barako.java
package com.javacodegeeks.example; public class Barako implements Seed { private String weight; public Barako(Arabica w1, CoffeeBean w2) { setWeight(w1.getWeight() + w2.getWeight()); } public void setWeight(String weight) { this.weight = weight; } @Override public String getWeight() { return this.weight; } @Override public String toString() { return "Barako [weight=" + weight + "]"; } }
CoffeeBean.java
package com.javacodegeeks.example; public class CoffeeBean { private String weight; public CoffeeBean() {} public CoffeeBean(String weight) { this.weight = weight; } public String getWeight() { return weight; } public void setWeight(String weight) { this.weight = weight; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((weight == null) ? 0 : weight.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; CoffeeBean other = (CoffeeBean) obj; if (weight == null) { if (other.weight != null) return false; } else if (!weight.equals(other.weight)) return false; return true; } @Override public String toString() { return "CoffeeBean [weight=" + weight + "]"; } }
5. Code Walkthrough
Our code below performs the basic operations of how to use JdbcBeanDefinitionReader
. Skim through the code below but peruse the explanation after it.
Main.java
package com.javacodegeeks.example; import java.sql.ResultSet; import java.sql.SQLException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowCallbackHandler; import org.springframework.jdbc.core.support.JdbcBeanDefinitionReader; @SpringBootApplication public class Main implements CommandLineRunner { @Autowired JdbcTemplate jdbcTemplate; public static void main(String[] args) { SpringApplication.run(Main.class, args); } public void run(String... arg0) throws Exception { System.out.println("Building tables"); jdbcTemplate.execute("DROP TABLE coffee_beans IF EXISTS"); jdbcTemplate.execute("CREATE TABLE coffee_beans(id SERIAL, beanName VARCHAR(255), property VARCHAR(255), value VARCHAR(255))"); System.out.println("\nCreating the Spring Beans..."); jdbcTemplate.update("INSERT INTO coffee_beans(beanName, property, value) VALUES (?, ?, ?)", "robusta", "(class)", "com.javacodegeeks.example.CoffeeBean"); jdbcTemplate.update("INSERT INTO coffee_beans(beanName, property, value) VALUES (?, ?, ?)", "robusta", "(abstract)", "false"); jdbcTemplate.update("INSERT INTO coffee_beans(beanName, property, value) VALUES (?, ?, ?)", "robusta", "weight", "1"); jdbcTemplate.update("INSERT INTO coffee_beans(beanName, property, value) VALUES (?, ?, ?)", "arabica", "(class)", "com.javacodegeeks.example.Arabica"); // must be fully qualified jdbcTemplate.update("INSERT INTO coffee_beans(beanName, property, value) VALUES (?, ?, ?)", "arabica", "$0", "2"); // inject 2 as the constructor argument jdbcTemplate.update("INSERT INTO coffee_beans(beanName, property, value) VALUES (?, ?, ?)", "barako", "(class)", "com.javacodegeeks.example.Barako"); jdbcTemplate.update("INSERT INTO coffee_beans(beanName, property, value) VALUES (?, ?, ?)", "barako", "$0(ref)", "arabica"); // inject arabica bean as the 0th constructor argument jdbcTemplate.update("INSERT INTO coffee_beans(beanName, property, value) VALUES (?, ?, ?)", "barako", "$1(ref)", "robusta"); // inject robusta bean as the 0th constructor argument jdbcTemplate.update("INSERT INTO coffee_beans(beanName, property, value) VALUES (?, ?, ?)", "barako", "(lazy-init)", "true"); // default is false. lazy initialization: delay 'expensive operation' until needed, store result so that 'expensive opearation isn't repeated readRecords(); DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); JdbcBeanDefinitionReader beanReader = new JdbcBeanDefinitionReader(beanFactory); beanReader.setJdbcTemplate(jdbcTemplate); beanReader.loadBeanDefinitions("SELECT beanName, property, value FROM coffee_beans"); // we don't want to include id System.out.println(); System.out.println("Number of Spring Beans in container: " + beanFactory.getBeanDefinitionCount()); CoffeeBean robusta = (CoffeeBean) beanFactory.getBean("robusta"); Seed arabica = (Seed) beanFactory.getBean("arabica"); Seed barako = (Seed) beanFactory.getBean("barako"); System.out.println("robusta: " + robusta); System.out.println("arabica: " + arabica); System.out.println("barako: " + barako); } private void readRecords() { System.out.println("Reading Spring Bean records..."); System.out.printf("%-30.30s %-30.30s %-30.30s%n", "Bean Name", "Property", "Value"); jdbcTemplate.query("SELECT * FROM coffee_beans", new RowCallbackHandler() { public void processRow(ResultSet rs) throws SQLException { System.out.printf("%-30.30s %-30.30s %-30.50s%n", rs.getString("beanName"), rs.getString("property"), rs.getString("value")); } }); } }
This article is about JdbcBeanDefinitionReader
so we’ll go straight to the run()
method. For explanations concerning Spring Boot and its annotations (e.g. @SpringBootApplication
, @Autowired
), have look at Spring Boot JDBC Example or Spring Boot and JPA Example.
First off, we created a database and populated it with a bean name, property and value. As stated above, JdbcBeanDefinitionReader
expects columns for bean name, property name and value as String
. The format for each are identical to the properties format recognized by PropertiesBeanDefinitionReader
. We can look at it this way:
properties file
robusta.(class)=com.javacodegeeks.example.CoffeeBean robusta.(abstract)=false robusta.weight=1 arabica.(class)=com.javacodegeeks.example.Arabica arabica.$0=2
What have we done? We have declared the robusta
bean as a class of CoffeeBean. abstract=true
means this bean can be instantiated directly and we have injected the weight value as 1. The arabica
bean is of type Arabica
and we have injected 2 as the first constructor argument. The barako
bean is of type Barako
and is injected with the arabica
and robusta
beans as its first and second constructor argument, respectively. Furtheremore, barako
is initialized lazily.
The JdbcBeanDefinitionReader
is typically applied to a DefaultListableBeanFactory
. We provide the bean reader a JdbcTemplate
and an SQL statement to load the bean definitions from the database. Any join and any other columns are permitted as long as the first three columns are bean name, property name and value.
The moment of truth. Once the bean definitions are loaded, we can check how many Spring Beans are in the container. We then get the beans we want and display them. Are the values correct? What do you think?
6. Spring JdbcBeanDefinitionReader Output
After running the code above (Run As -> Java Application), we should have an output that looks like the one below.
Console Output
Building tables Creating the Spring Beans... Reading Spring Bean records... Bean Name Property Value robusta (class) com.javacodegeeks.example.CoffeeBean robusta (abstract) false robusta weight 1 arabica (class) com.javacodegeeks.example.Arabica arabica $0 2 barako (class) com.javacodegeeks.example.Barako barako $0(ref) arabica barako $1(ref) robusta barako (lazy-init) true Number of Spring Beans in container: 3 robusta: CoffeeBean [weight=1] arabica: Arabica [weight=2] barako: Barako [weight=21]
As we can clearly see, our beans have been defined and loaded correctly. Their corresponding weight values are correct. Job done.
7. Spring JdbcBeanDefinitionReader Summary
In summary, we include the spring-boot-starter-jdbc
dependency to make available all the Spring modules we need to make JDBC operations. We then add the database dependency, in this case H2. We then passed JdbcTemplate
to JdbcBeanDefinitionReader
and supplied the SQL select statement. Once the beans were in the Spring container, we acquired it through the DefaultListableBeanFactory
. That’s all folks.
8. Download the Source Code
This is an example about Spring JdbcBeanDefinitionReader.
You can download the source code of this example here: spring-jdbcbeandefinitionreader.zip.