Introduction

JNDI data source is very similar to JDBC data source. 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 we do with JDBC data sources, we only need to specify the JNDI resource name in our application server.

Why do we use JNDI DataSource

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.

You may also like to read Spring Data JPA CRUD Example and Spring Data JPA Entity Graphs

Prerequisites

Knowledge of JPA, Spring, Java

Softwares

JDK 8
Gradle 4
Eclipse

Example

In this example we will create Spring Boot application. We will use Spring Data JPA for quering database.

Creating project in Eclipse

Create Gradle based Spring Boot project called SpringBootJndiDataSource in Eclipse using below build.gradle script

buildscript {
	ext {
		springBootVersion = '1.5.9.RELEASE'
	}
    repositories {
    	mavenLocal()
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'

jar {
    baseName = 'SpringBootJndiDataSource'
    version = '0.0.1-SNAPSHOT'
    manifest {
        attributes("Main-Class": "com.jeejava.main.Application")
    }
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

configurations {
    all*.exclude module : 'spring-boot-starter-logging'
}

repositories {
	mavenLocal()
    mavenCentral()
}
    
dependencies {
	compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
	compile('org.springframework.boot:spring-boot-starter-data-jpa')
	runtime('com.oracle.jdbc:ojdbc7:12.1.0.2')
    compile("org.slf4j:slf4j-api")
    compile("org.slf4j:slf4j-log4j12")
    compile("commons-logging:commons-logging:1.2")
}

Build the blank project, if you get any Exception related to main class then create a class called Application under package com.jeejava.main with main method and try to build.

Create below application.properties file under src/main/resources directory.

#start server at below port
server.port=9999

#DB connection parameters
connection.url=jdbc:Oracle:thin:@//:/
connection.username=
connection.password=
connection.driverClassName=oracle.jdbc.driver.OracleDriver
connection.jndiName=jdbc/oracleDataSource

#DB dialect - override default one
spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect

Create below properties class DbConnectionProps to load key/value pairs for database connection parameters from application.properties file.

package com.jeejava.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "connection")
public class DbConnectionProps {
	String url;
	String username;
	String password;
	String driverClassName;
	String jndiName;

	//getters and setters
}

Create ApplicationConfig class in order to create Spring Beans such as we need for DbConnectionProps.

package com.jeejava.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.jeejava.properties.DbConnectionProps;

@Configuration
public class ApplicationConfig {
	@Bean
	public DbConnectionProps dbConnectionProps() {
		return new DbConnectionProps();
	}
}

Now create the DataSourceConfig class in order to configure DataSource and JPA Repository with Spring’s Transaction support.

package com.jeejava.config;

import java.sql.SQLException;
import javax.naming.NamingException;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.descriptor.web.ContextResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jndi.JndiObjectFactoryBean;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.jeejava.properties.DbConnectionProps;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.jeejava.jparepository")
public class DataSourceConfig {

	@Autowired
	DbConnectionProps props;

	@Bean
	public TomcatEmbeddedServletContainerFactory tomcatFactory() {
		return new TomcatEmbeddedServletContainerFactory() {
			@Override
			protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat) {
				tomcat.enableNaming();
				return super.getTomcatEmbeddedServletContainer(tomcat);
			}
			@Override
			protected void postProcessContext(Context context) {
				ContextResource resource = new ContextResource();
				resource.setName(props.getJndiName());
				resource.setType(DataSource.class.getName());
				resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
				resource.setProperty("driverClassName", props.getDriverClassName());
				resource.setProperty("url", props.getUrl());
				resource.setProperty("password", props.getPassword());
				resource.setProperty("username", props.getUsername());
				context.getNamingResources().addResource(resource);
			}
		};
	}

	@Bean(destroyMethod = "")
	public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
		JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
		bean.setJndiName("java:comp/env/" + props.getJndiName());
		bean.setProxyInterface(DataSource.class);
		bean.setLookupOnStartup(false);
		bean.afterPropertiesSet();
		return (DataSource) bean.getObject();
	}

	@Bean
	public EntityManagerFactory entityManagerFactory() throws SQLException, IllegalArgumentException, NamingException {
		HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
		vendorAdapter.setDatabase(Database.ORACLE);
		vendorAdapter.setShowSql(true);
		LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
		factory.setJpaVendorAdapter(vendorAdapter);
		factory.setPackagesToScan("com.jeejava.model");
		factory.setDataSource(jndiDataSource());
		factory.afterPropertiesSet();
		return factory.getObject();
	}

	@Bean
	public PlatformTransactionManager transactionManager()
									throws SQLException, IllegalArgumentException, NamingException {
		JpaTransactionManager txManager = new JpaTransactionManager();
		txManager.setEntityManagerFactory(entityManagerFactory());
		return txManager;
	}
}

Create below entity class LogDetails to define mapping to database table. Please create the table with below columns as found in the below class.

package com.jeejava.model;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "LOG_DETAILS")
public class LogDetails implements Serializable {
	private static final long serialVersionUID = -2711129826239216746L;
	@Id
	@Column(name = "ID")
	Long id;
	@Column(name = "LOG_NAME")
	String logName;
	@Column(name = "DESCRIPTION")
	String logDetails;
	
	//getters and setters
}

Create below JPA Repository interface to perform database activities.

package com.jeejava.jparepository;

import org.springframework.data.jpa.repository.JpaRepository;
import com.jeejava.model.LogDetails;

public interface LogDetailsRepository extends JpaRepository<LogDetails, Long> {

}

We need below Service class to fetch data from JPA Repository DAO layer.

package com.jeejava.service;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.jeejava.jparepository.LogDetailsRepository;
import com.jeejava.model.LogDetails;

@Service
public class LogDetailsService {
	@Autowired
	LogDetailsRepository repository;
	public List<LogDetails> getLogDetails() {
					return repository.findAll();
	}
}

Need to send data to client through below Rest Controller class.

package com.jeejava.rest.controller;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.jeejava.model.LogDetails;
import com.jeejava.service.LogDetailsService;

@RestController
public class LogDetailsRestController {
	@Autowired
	LogDetailsService service;
	@GetMapping("/logs")
	public ResponseEntity<List<LogDetails>> getlogDetails() {
		return new ResponseEntity<>(service.getLogDetails(), HttpStatus.OK);
	}
}

Create main class to start up the application

package com.jeejava.main;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages = "com.jeejava")
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

Thanks for reading.

Tags:

I am a professional Web developer, Enterprise Application developer, Software Engineer and Blogger. Connect me on Roy Tutorials | TwitterFacebook Google PlusLinkedin | Reddit

Leave a Reply

Your email address will not be published. Required fields are marked *