Configure JNDI DataSource with Spring Boot
JNDI Data Source
In this post I will show you how to configure JNDI datasource with Spring Boot applications.
JNDI (Java Naming Directory Interface) data source is very similar to JDBC (Java Database Connectivity) data source. I will show examples on Oracle as well as MySQL database servers. The MySQL version example is downloadable at the end of this tutorial.I will also show you how to work with Spring Boot framework for JNDI data source. The TomcatEmbeddedServletContainerFactory has been removed from Spring Boot 2 and I will show you how to use TomcatServletWebServerFactory in Spring Boot 2 and Spring Boot 3.
The JNDI data source accesses a database connection that is pre-defined and configured in the application server and published as a JNDI resource or service. Instead of specifying a driver and database as you do with JDBC data sources, you only need to specify the JNDI resource name in our application server.
Why do you need JNDI Data Source?
JNDI comes in rescue when you have to move an application between environments: development ->integration ->test ->production.
If you configure each application server to use the same JNDI name, you can have different databases in each environment but you need not to change your code. You just need to drop the deployable WAR file in the new environment.
Related Posts:
Prerequisites
Java 1.8+(12/19), Spring Boot 2.2.1 to 2.4.2/Spring Boot 3.3.3, Maven 3.8.5/3.9.8, MySQL 8.0.x/8.1.0
Project Setup
Create a maven based Spring Boot project called spring-boot-jndi-datasource in your favorite tool or IDE.
Build File
As it is a REST based application, so I have added starter-web. I included starter-data-jpa to perform to perform database operations.
Notice in the build file I have added jaxb-api. This API is required by the Java application after Java version 9 or higher to avoid JAXB related exceptions.
For maven based project you can use the following pom.xml file. According to your Spring Boot version, you can update the dependency version.
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.roytuts</groupId>
<artifactId>spring-boot-jndi-datasource</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>19</maven.compiler.source>
<maven.compiler.target>19</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project> Application Configuration
Create below application.properties file under src/main/resources directory.
Oracle Database
I have specified Oracle Database connection details and hibernate dialect.
#datasource
app.datasource.driverClassName=oracle.jdbc.driver.OracleDriver
app.datasource.url=jdbc:Oracle:thin:@//:/
app.datasource.username=scott
app.datasource.password=tiger
app.datasource.jndiName=jdbc/myDataSource
#disable schema generation from Hibernate
spring.jpa.hibernate.ddl-auto=none
#DB dialect - override default one
spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect MySQL Database
I have specified MySQL Database connection details.
#datasource
app.datasource.driverClassName=com.mysql.cj.jdbc.Driver
app.datasource.url=jdbc:mysql://localhost/roytuts
app.datasource.username=root
app.datasource.password=root
app.datasource.jndiName=jdbc/myDataSource
#disable schema generation from Hibernate
spring.jpa.hibernate.ddl-auto=none Remember to update the configuration values according to your own.
Property Config
Create below properties class DatabaseProperties to load key/value pairs for database connection parameters from application.properties file.
In the property file you have all properties declared with a prefix – spring.datasource. Therefore using Spring Boot it is very easy to load properties in Java class attributes. Simply specify the prefix using @ConfigurationProperties annotation and add the same property names as class attributes.
package com.roytuts.spring.boot.jndi.datasource.config;
@ConfigurationProperties(prefix = "app.datasource")
public class DatabaseProperties {
String url;
String username;
String password;
String driverClassName;
String jndiName;
// getters and setters
} I have loaded properties in the above configuration class but I won’t be able to inject as a Bean until I declare as a Bean.
So I will declare as a Bean to access the property config class throughout the application wherever required.
Application Config
Now create the AppConfig class in order to configure DataSource and JPA Repository with Spring’s Transaction support. I will also expose the property config as a bean in this class.
package com.roytuts.spring.boot.jndi.datasource.config;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.roytuts.spring.boot.jndi.datasource.repository")
public class AppConfig {
@Bean
public DatabaseProperties databaseProperties() {
return new DatabaseProperties();
}
@Bean
public TomcatServletWebServerFactory tomcatFactory() {
return new TomcatServletWebServerFactory() {
@Override
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
tomcat.enableNaming();
return super.getTomcatWebServer(tomcat);
}
@Override
protected void postProcessContext(Context context) {
ContextResource resource = new ContextResource();
resource.setType(DataSource.class.getName());
resource.setName(databaseProperties().getJndiName());
resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
resource.setProperty("driverClassName", databaseProperties().getDriverClassName());
resource.setProperty("url", databaseProperties().getUrl());
resource.setProperty("username", databaseProperties().getUsername());
resource.setProperty("password", databaseProperties().getPassword());
context.getNamingResources().addResource(resource);
}
};
}
@Bean(destroyMethod = "")
public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName("java:comp/env/" + databaseProperties().getJndiName());
bean.setProxyInterface(DataSource.class);
// bean.setResourceRef(true);
bean.setLookupOnStartup(false);
bean.afterPropertiesSet();
return (DataSource) bean.getObject();
}
@Bean
public EntityManagerFactory entityManagerFactory(DataSource dataSource)
throws SQLException, IllegalArgumentException, NamingException {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
// vendorAdapter.setDatabase(Database.ORACLE);
vendorAdapter.setDatabase(Database.MYSQL);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.roytuts.spring.boot.jndi.datasource.entity");
factory.setDataSource(dataSource);
factory.afterPropertiesSet();
return factory.getObject();
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory)
throws SQLException, IllegalArgumentException, NamingException {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
} Of course according to your database type and version you may need to work on the above configuration class.
Entity Class
The below entity class Company is created to define mapping to database table. Please create the table with below columns as found in the below class.
package com.roytuts.spring.boot.jndi.datasource.entity;
@Entity
@Table(name = "company")
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "name")
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
} MySQL Table
The corresponding MySQL table structure is given below:
CREATE TABLE `company` (
`id` int unsigned COLLATE utf8mb4_unicode_ci NOT NULL AUTO_INCREMENT,
`name` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; Dump Some Data
As you need to test your application, so dump some data:
insert into `company`(`id`,`name`) values
(1,'Tom & Jerry'),
(2,'Order All'),
(3,'Akash Food'),
(4,'Chinese Food'),
(5,'Roy Food'); Spring JPA Repository
Create below JPA Repository interface to perform database activities. Spring provides built-in API through JpaRepository to perform basic CRUD operations.
package com.roytuts.spring.boot.jndi.datasource.repository;
public interface CompanyRepository extends JpaRepository<Company, Long> {
} Spring Service
You need below Service class to fetch data from JPA Repository DAO layer.
package com.roytuts.spring.boot.jndi.datasource.service;
@Service
public class CompanyService {
@Autowired
private CompanyRepository companyRepository;
public List<Company> getCompanyList() {
return companyRepository.findAll();
}
} Spring REST Controller
Need to send data to client through below Rest Controller class.
package com.roytuts.spring.boot.jndi.datasource.rest.controller;
@RestController
public class CompanyRestController {
@Autowired
private CompanyService companyService;
@GetMapping("/company")
public ResponseEntity<List<Company>> getCompanyList() {
return new ResponseEntity<List<Company>>(companyService.getCompanyList(), HttpStatus.OK);
}
} Spring Main Class
Create main class to start up the application. main class with @SpringBootApplication annotation is enough to deploy the application into Tomcat server.
package com.roytuts.spring.boot.jndi.datasource;
@SpringBootApplication
public class JndiDatasourceApp {
public static void main(String[] args) {
SpringApplication.run(JndiDatasourceApp.class, args);
}
} Testing the Application
Running the above main class will deploy the application into embedded Tomcat server.
Now hit the URL http://localhost:8080/company from browser or REST client you will get the below output:
[{"id":1,"name":"Tom & Jerry"},{"id":2,"name":"Order All"},{"id":3,"name":"Akash Food"},{"id":4,"name":"Chinese Food"},{"id":5,"name":"Roy Food"}] That’s all. Hope you got an idea how to work with JNDI data source in Spring Boot.
No comments
Leave a comment