spring

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

  1. Apache Maven
  2. Mars Eclipse
  3. Spring Boot
  4. H2 Database Engine
  5. JdbcBeanDefinitionReader Javadoc

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.

Download
You can download the source code of this example here: spring-jdbcbeandefinitionreader.zip.

Joel Patrick Llosa

I graduated from Silliman University in Dumaguete City with a degree in Bachelor of Science in Business Computer Application. I have contributed to many Java related projects at Neural Technologies Ltd., University of Southampton (iSolutions), Predictive Technologies, LLC., Confluence Service, North Concepts, Inc., NEC Telecom Software Philippines, Inc., and NEC Technologies Philippines, Inc. You can also find me in Upwork freelancing as a Java Developer.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button