spring-boot plain jdbc with mysql (no jpa) - mysql

I want to create a spring-boot project using plain jdbc as descripted here : https://spring.io/guides/gs/relational-data-access/.
I would like to use a mysql database instead of the h2 of the guide. Here are my config files :
build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.5.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
bootJar {
baseName = 'gs-relational-data-access'
version = '0.1.0'
}
repositories {
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter
compile group: 'org.springframework.boot', name: 'spring-boot-starter', version: '2.1.0.RELEASE'
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc
compile group: 'org.springframework.boot', name: 'spring-boot-starter-jdbc', version: '2.1.0.RELEASE'
// https://mvnrepository.com/artifact/mysql/mysql-connector-java
compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.47'
testCompile("junit:junit")
application.properties
# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url=jdbc:mysql://localhost:3306/spring-jdbc?useSSL=false
spring.datasource.username=root
spring.datasource.password=***
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto=create
Application
package hello;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
#SpringBootApplication
public class Application implements CommandLineRunner {
private static final Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String args[]) {
SpringApplication.run(Application.class, args);
}
#Autowired
JdbcTemplate jdbcTemplate;
#Override
public void run(String... strings) throws Exception {
log.info("Creating tables");
jdbcTemplate.execute("DROP TABLE customers IF EXISTS");
jdbcTemplate.execute("CREATE TABLE customers(" +
"id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))");
// Split up the array of whole names into an array of first/last names
List<Object[]> splitUpNames = Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long").stream()
.map(name -> name.split(" "))
.collect(Collectors.toList());
// Use a Java 8 stream to print out each tuple of the list
splitUpNames.forEach(name -> log.info(String.format("Inserting customer record for %s %s", name[0], name[1])));
// Uses JdbcTemplate's batchUpdate operation to bulk load data
jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames);
log.info("Querying for customer records where first_name = 'Josh':");
jdbcTemplate.query(
"SELECT id, first_name, last_name FROM customers WHERE first_name = ?", new Object[] { "Josh" },
(rs, rowNum) -> new Customer(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name"))
).forEach(customer -> log.info(customer.toString()));
}
}
But the project fail to start and suggest :
Consider defining a bean of type 'org.springframework.jdbc.core.JdbcTemplate' in your configuration.
What could I do?

Related

Storage solution for persisting the API’s data

I've made a CRUD REST API with Spring Boot, Spring Data JPA and MySQL and everything's okay.
I'm using postman aswell, and everything seems okay, it is connected to my sqldb, it creates in my sql, updates, retrieve and deletes, but whenever I stop it, and start it again, all my data once created is gone.
I need a solution for persisting the API's data.
My application.yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_vendor?useSSL=false
username: root
password: root
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER
# The SQL dialect makes Hibernate generate better SQL for the chosen database
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
#JPA Settings
jpa.hibernate.ddl_auto: create
My CloudVendorController.java
package com.thinkcon.demo.controller;
import com.thinkcon.demo.model.CloudVendor;
import com.thinkcon.demo.service.CloudVendorService;
import java.util.List;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/cloudvendor")
public class CloudVendorController {
CloudVendorService cloudVendorService;
public CloudVendorController(CloudVendorService cloudVendorService)
{
this.cloudVendorService = cloudVendorService;
}
#GetMapping("{vendorId}")
public CloudVendor getCloudVendorDetails(#PathVariable("vendorId") String vendorId){
return cloudVendorService.getCloudVendor(vendorId);
}
#GetMapping()
public List<CloudVendor> getAllCloudVendorDetails(){
return cloudVendorService.getAllCloudVendors();
}
#PostMapping
public String createCloudVendorDetails(#RequestBody CloudVendor cloudVendor)
{
cloudVendorService.createCloudVendor(cloudVendor);
return "CloudVendor Created Successfully";
}
#PutMapping
public String updateCloudVendorDetails(#RequestBody CloudVendor cloudVendor)
{
cloudVendorService.updateCloudVendor(cloudVendor);
return "CloudVendor updated Successfully";
}
#DeleteMapping("{vendorId}")
public String deleteCloudVendorDetails(#PathVariable("vendorId")String vendorId)
{
cloudVendorService.deleteCloudVendor(vendorId);
return "CloudVendor deleted Successfully";
}
}
And my model\CloudVendor.java
package com.thinkcon.demo.model;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
#Entity
#Table(name="cloud_vendor_info")
public class CloudVendor
{
#Id
private String vendorId;
private String vendorName;
private String vendorAddress;
private String vendorPhoneNumber;
public CloudVendor(String vendorId, String vendorName, String vendorAddress, String vendorPhoneNumber) {
this.vendorId = vendorId;
this.vendorName = vendorName;
this.vendorAddress = vendorAddress;
this.vendorPhoneNumber = vendorPhoneNumber;
}
public CloudVendor() {
}
public String getVendorId() {
return vendorId;
}
public String getVendorName() {
return vendorName;
}
public String getVendorAddress() {
return vendorAddress;
}
public String getVendorPhoneNumber() {
return vendorPhoneNumber;
}
public void setVendorId(String vendorId) {
this.vendorId = vendorId;
}
public void setVendorName(String vendorName) {
this.vendorName = vendorName;
}
public void setVendorAddress(String vendorAddress) {
this.vendorAddress = vendorAddress;
}
public void setVendorPhoneNumber(String vendorPhoneNumber) {
this.vendorPhoneNumber = vendorPhoneNumber;
}
}
I need the solution, I can't have my data being restarted all over again everytime I just end it.
There are 5 types ;
create -> drops existing tables, then creates new tables.
update -> creates, compares with old ones, if there are changes,
then updates and never deletes the existing tables or columns.
create-drop -> similar to create, but it will drop the database
after shutdown.
validate -> validates whether the tables and columns exist,
otherwise it throws an exception for that.
none -> turns off the DDL generation.
So, you will need ;
jpa.hibernate.ddl_auto: update

table doesnot exist in while connect to database in springbatch

I have an user.csv file .I want to store the data of csv file into sql
database. I provide proper sql connection in yml file but it throws
run time exception
Caused by: java.sql.SQLSyntaxErrorException: Table 'login.batch_job_instance' doesn't exist
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120) ~[mysql-connector-java-8.0.26.jar:8.0.26]
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.26.jar:8.0.26]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953) ~[mysql-connector-java-8.0.26.jar:8.0.26]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeQuery(ClientPreparedStatement.java:1003) ~[mysql-connector-java-8.0.26.jar:8.0.26]
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52) ~[HikariCP-4.0.3.jar:na]
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java) ~[HikariCP-4.0.3.jar:na]
at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:722) ~[spring-jdbc-5.3.10.jar:5.3.10]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:651) ~[spring-jdbc-5.3.10.jar:5.3.10]
... 42 common frames omitted
2021-09-27 16:31:27.913 INFO 3800 --- [ main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2021-09-27 16:31:27.915 INFO 3800 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2021-09-27 16:31:27.922 INFO 3800 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
Process finished with exit code 1
batch.config
package com.nilmani.literalmission.config
import com.nilmani.literalmission.model.User
import org.springframework.batch.core.Job
import org.springframework.batch.core.Step
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory
import org.springframework.batch.core.launch.support.RunIdIncrementer
import org.springframework.batch.item.ItemProcessor
import org.springframework.batch.item.ItemReader
import org.springframework.batch.item.ItemWriter
import org.springframework.batch.item.file.FlatFileItemReader
import org.springframework.batch.item.file.LineMapper
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper
import org.springframework.batch.item.file.mapping.DefaultLineMapper
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.io.FileSystemResource
#Configuration
#EnableBatchProcessing
class BatchConfig {
#Bean
fun job(jobBuilderFactory: JobBuilderFactory,
stepBuilderFactory: StepBuilderFactory,
itemReader: ItemReader<User>,
itemProcessor: ItemProcessor<User,User>,
itemWriter: ItemWriter<User>
):Job{
val step: Step = stepBuilderFactory["ETL-file-load"]
.chunk<User, User>(100)
.reader(itemReader)
.processor(itemProcessor)
.writer(itemWriter)
.build()
return jobBuilderFactory["ETL-Load"]
.incrementer(RunIdIncrementer())
.start(step)
.build()
}
#Bean
fun itemReader(): FlatFileItemReader<User>? {
val flatFileItemReader: FlatFileItemReader<User> = FlatFileItemReader<User>()
flatFileItemReader.setResource(FileSystemResource("src/main/resources/users.csv"))
flatFileItemReader.setName("CSV-Reader")
flatFileItemReader.setLinesToSkip(1)
flatFileItemReader.setLineMapper(lineMapper()!!)
return flatFileItemReader
}
#Bean
fun lineMapper(): LineMapper<User>? {
val defaultLineMapper: DefaultLineMapper<User> = DefaultLineMapper<User>()
val lineTokenizer = DelimitedLineTokenizer()
lineTokenizer.setDelimiter(",")
lineTokenizer.setStrict(false)
lineTokenizer.setNames("id", "name", "dept", "salary")
val fieldSetMapper: BeanWrapperFieldSetMapper<User> = BeanWrapperFieldSetMapper<User>()
fieldSetMapper.setTargetType(User::class.java)
defaultLineMapper.setLineTokenizer(lineTokenizer)
defaultLineMapper.setFieldSetMapper(fieldSetMapper)
return defaultLineMapper
}
}
DBWriter.kt
package com.nilmani.literalmission.batch
import com.nilmani.literalmission.model.User
import com.nilmani.literalmission.repository.UserRepository
import org.springframework.batch.item.ItemWriter
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
#Component
class DBWriter :ItemWriter<User> {
#Autowired
private lateinit var userRepository: UserRepository
#Autowired
fun DBWriter(userRepository: UserRepository){
this.userRepository = userRepository
}
#Throws(Exception::class)
override fun write(users: MutableList<out User>) {
println("Data Saved for Users: $users")
userRepository.saveAll(users)
}
}
itemprocessor.kt
package com.nilmani.literalmission.batch
import com.nilmani.literalmission.model.User
import org.springframework.batch.item.ItemProcessor
import org.springframework.stereotype.Component
import java.util.*
import kotlin.collections.HashMap
#Component
class Processor : ItemProcessor<User,User> {
private val DEPT_NAMES: MutableMap<String, String> = HashMap()
fun Processor() {
DEPT_NAMES["100"] = "Technology"
DEPT_NAMES["102"] = "Operations"
DEPT_NAMES["103"] = "Accounts"
DEPT_NAMES["104"] = "Devloper"
}
#Throws(Exception::class)
override fun process(user: User): User? {
val deptCode:String=user.dept
val dept=DEPT_NAMES[deptCode]
if (dept != null) {
user.dept = dept
}
user.time= Date()
println(String.format("Converted from [%s] to [%s]", deptCode, dept))
return user
}
}
LoadController.kt
package com.nilmani.literalmission.controller
import org.springframework.batch.core.*
import org.springframework.batch.core.launch.JobLauncher
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException
import org.springframework.batch.core.repository.JobRestartException
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
#RestController
#RequestMapping("/load")
class LoadController {
#Autowired
private lateinit var jobLauncher: JobLauncher
#Autowired
private lateinit var job:Job
#GetMapping
#Throws(
JobParametersInvalidException::class,
JobExecutionAlreadyRunningException::class,
JobRestartException::class,
JobInstanceAlreadyCompleteException::class
)
fun load(): BatchStatus {
val maps: MutableMap<String, JobParameter> = HashMap()
maps["time"] = JobParameter(System.currentTimeMillis())
val parameters = JobParameters(maps)
val jobExecution: JobExecution = jobLauncher.run(job, parameters)
System.out.println("JobExecution: " + jobExecution.status)
println("Batch is Running...")
while (jobExecution.isRunning) {
println("...")
}
return jobExecution.status
}
}
User.kt
package com.nilmani.literalmission.model
import java.util.*
import javax.persistence.Entity
import javax.persistence.Id
#Entity
data class User(
#Id
val id:Long=0,
val name:String="",
var dept:String="",
val salary:String="",
var time:Date
)
I added every thing properly how I get this type of issue
I added all the gradel depedency ,I donot know where I make mistakes I
provide all the database connection releated stuff
Here my database connection details
spring:
datasource:
url: jdbc:mysql://localhost:3306/login
driverClassName: com.mysql.cj.jdbc.Driver
username: root
password:
jpa:
database-platform: org.hibernate.dialect.MySQL5Dialect
show-sql: true
hibernate:
ddl-auto: update
According to the error Table 'login.batch_job_instance' doesn't exist, you need to create Spring Batch tables in the database before running your job.
You can do that either manually by execution the DDL script for MySQL, or if you use Spring Boot, set the property spring.batch.initialize-schema=always.

JDBI json response o MySql database

i´m trying to use a endpoint to question mysql database in eclipse using tomcat 7 as server but it´s always giving me this error, does someone solved this problem with jdbi
type Exception report
message java.sql.SQLException: No suitable driver found for
jdbc:mysql://127.0.0.1/demo
The code:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import org.json.JSONException;
import org.json.JSONObject;
import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.Handle;
#Path("/jdbiservice")
public class JdbiService {
#Path("{f}")
#GET
#Produces("application/json")
public Response convertFtoCfromInput(#PathParam("f") int f) throws JSONException {
DBI dbi = new DBI("jdbc:mysql://127.0.0.1/demo", "user", "pass");
Handle h = dbi.open();
BatchExample b = h.attach(BatchExample.class);
Something s =b.findById(f);
h.close();
JSONObject jsonObject = new JSONObject(s);
String result = jsonObject.toString();
return Response.status(200).entity(result).build();
}
}
Hi have the jar connector file on the eclipse project path and inside tomcat lib folder.
This worked for me
package com.crunchify.restjersey;
import java.util.List;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import org.json.*;
import org.skife.jdbi.v2.*;
#Path("/sensorservice")
public class SensorService {
#Path("{id}")
#DELETE
public Response deleteSensorById(#PathParam("id") int id) {
///...
try {
DBI dbi = new DBI(SensorService.getDataSource());
Handle h = dbi.open();
SensorInterface si = h.attach(SensorInterface.class);
si.deleteById(id);;
h.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String result = "Deleted";
return Response.status(200).entity(result).build();
}
private static DataSource getDataSource (){
DataSource ds = null;
InitialContext contex;
try {
contex = new InitialContext();
ds = ( DataSource) contex.lookup("java:comp/env/jdbc/jndiname");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ds;
}
}
at webinf/webxml
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/mysql</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
on tomcat context file
<Resource
name = "jdbc/jndiname"
auth = "Container"
type = "javax.sql.DataSource"
maxActive ="100"
maxIdle = "30"
maxWait = "10000"
driverClassName = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://localhost:3306/schema"
username = "user"
password = "pass"
/>
You should include the mysql driver in your dependencies.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>

How to test an EJB service class that invokes a REST call?

I'm aware that some developer say it's not a unit test to invoke a method of an EJB which requests a web resource. However, please do not argue about that in this thread! I think it is worthwhile to do it.
my Test: testng class --> EJB method --> rest resource
Setup:
Wildfly 8.1
TestNG 6.8.8
jboss-jaxrs-api_1.1_spec, 1.0.1.Final
This is my testing class.
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import com.doe.webapp.model.general.geoinfo.GeoInfo;
public class GeoIPBeanTest {
#DataProvider(name = "ipAdresses")
public static Object[][] primeNumbers() {
return new Object[][] {
{ "127.0.0.1", true }, // localhost
{ "80.218.114.61", true } }; // real IP
}
#Test(dataProvider = "ipAdresses")
public void getGeoInfoByIp(String ipAddress, boolean isExpectedTrue) {
GeoIPBean geoIpBean = new GeoIPBean();
GeoInfo geoInfo = null;
try {
geoInfo = geoIpBean.getGeoInfoByIp(ipAddress);
} catch (Exception ex) {
Assert.fail(ex.getLocalizedMessage());
}
}
}
This is my class under test.
import javax.ejb.Singleton;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import com.doe.webapp.model.general.geoinfo.GeoInfo;
#Singleton
public class GeoIPBean {
private static final String IPV4_PATTERN = "^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";
Map<String, GeoInfo> geoInfoCache = new HashMap<String, GeoInfo>();
// Service Description is here http://freegeoip.net/
static final String GEO_SERICE_URL = "http://freegeoip.net/";
static final String FORMAT = "json";
public GeoInfo getGeoInfoByIp(String ipAddress) {
if(!isValidIp(ipAddress)){
//TODO log invalid IP as warning
return null;
}
GeoInfo geoInfo = geoInfoCache.get(ipAddress);
if (geoInfo == null) {
Client client = ClientBuilder.newClient();
// Invoke the service.
WebTarget webTarget = client.target(GEO_SERICE_URL + FORMAT + "/"
+ ipAddress);
//geoInfo
Builder builder = webTarget.request(MediaType.APPLICATION_JSON);
geoInfo = builder.get(GeoInfo.class);
}
return geoInfo;
}
public static boolean isValidIp(String ipAddress) {
if(ipAddress == null)
return false;
Pattern pattern = Pattern.compile(IPV4_PATTERN);
Matcher matcher = pattern.matcher(ipAddress);
return matcher.matches();
}
}
This EJB works when I run it in the container. It does NOT work in a testNG case.
Client client = ClientBuilder.newClient();
This line in the EJB returnS the error.
java.lang.AssertionError: java.lang.ClassNotFoundException: org.glassfish.jersey.client.JerseyClientBuilder
at org.testng.Assert.fail(Assert.java:94)
at com.doe.webapp.service.general.geoinfo.GeoIPBeanTest.getGeoInfoByIp(GeoIPBeanTest.java:25)
I first thought it is because I have annotated the wildfly library with scope provided ...
<!-- JBOSS JAX REST 2.0 FRAMEWORK -->
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
<artifactId>jboss-jaxrs-api_1.1_spec</artifactId>
<version>1.0.1.Final</version>
</dependency>
<!-- RS client library -->
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
</dependency>
... but Wildfly is using RestEasy and not Jersey. Then I added ...
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.18.1</version>
</dependency>
... but did NOT help neither.
In Wildfly, they have been using RESTEasy as the default rest provider. It states,
RESTEasy is bundled with JBoss/Wildfly and completely integrated as per the requirements of Java EE 6.
Therefore you have to remove RESTEasy dependencies defore you use Jersey with Jboss/Wildfly. Eventhough I haven't done that, there can be many resources which guides to do that. (Check these links.)
Or else, as an alternative, you can use RESTEasy instead of Jersey.

Configure SSL certificates with Hibernate, Spring and JDBC

I'm trying to move from an unencrypted JDBC connection using a username and password to log in to my MySQL database server, to a connection using SSL and certificate-based authentication. I'm using Hibernate with Spring MVC. My WebAppConfig file looks like this:
package com.****.PolicyManager.init;
import java.util.Properties;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.JstlView;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
#Configuration
#ComponentScan("com.sprhib")
#EnableWebMvc
#EnableTransactionManagement
#PropertySource("classpath:application.properties")
public class WebAppConfig {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_DATABASE_URL = "db.urlSSL";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";
#Resource
private Environment env;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
return dataSource;
}
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setPackagesToScan(env.getRequiredProperty(
PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
sessionFactoryBean.setHibernateProperties(hibProperties());
return sessionFactoryBean;
}
private Properties hibProperties() {
Properties properties = new Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT,
env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL,
env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
return properties;
}
#Bean
public HibernateTransactionManager transactionManager() {
HibernateTransactionManager transactionManager =
new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
#Bean
public UrlBasedViewResolver setupViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/pages/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
}
And my properties config file (application.properties) as follows:
#DB properties:
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/PolicyManager
db.urlSSL=jdbc:mysql://localhost:3306/PolicyManager?autoReconnect=true&verifyServerCertificate=false&useSSL=true&requireSSL=true
db.username=myuser
db.password=mypass
#Hibernate Configuration:
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
hibernate.show_sql=true
entitymanager.packages.to.scan=com.****.PolicyManager.model
I've generated the right certificates inside /etc/mysql/certs and have edited my.cnf to point to then, but can't find any info online about how to configure my specific method of database initialisation to use certificate-based authentication to remove the need to store my database username and password in plain text on the server.
Can anyone suggest a solution or point me to a tutorial that uses this WebAppConfig.java file (hib properties, DriverManagerDataSource and LocalSessionFactoryBean) for it's configuration?
The MySQL guide has information on what to do on the client side, this bug also has some detailed information.
It basically comes done to the following steps
Create a keystore and truststore with your clients certificate
Configure your environment (or a MysqlDataSource) to use these keystore and truststore
Configure the connection URL properly (which is what you apparently already have done).
And that should be it. The key is to have the correct certificates on the client side.
More information:
Secure JDBC connection to MySQL from GlassFish
Secure JDBC connection to MySQL from Java
MySQL SSL Documentation