Am trying to do simple JDBC call in a spring boot application is not working - spring-boot

I am looking forward to creating a service using spring boot application where I like to use JDBC prepared statement call which executes the stored procedure get me the required result.
I like to have connection pooling but unfortunately, I don’t know implement
Summary
(services using spring boot --->Simple JDBC with connection pooling---->Mysql)
For this, I have tried to create a data source and execute jdbc statement but not working
#Controller
public class ExampleController {
#Autowired
private ExampleRepository repo;
#RequestMapping("/")
public #ResponseBody String getDataBaseData() throws SQLException{
return repo.getDataBaseData();
}
}
#Configuration
public class DataSources {
#Bean(name = "primary")
#Primary
#ConfigurationProperties(prefix="spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
}
#Component
public class ExampleRepository {
#Autowired
private DataSource ds;
public String getDataBaseData() throws SQLException {
Connection con = ds.getConnection();
System.out.println(con);
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("select * from emp");
while (rs.next())
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getString(3));
con.close();
return rs.toString();
}
}
getting errors like below
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.
Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
Expected Result : Database data should display in web browser
This is my github repo https://github.com/PradeepKumarHE/SpringBootWithSimpleJDBC/tree/master
where i have DBscript file to create

What I can see from your pom.xml, you are using spring-boot-starter-data-jpa. It fetches unneccessary dependencies, which triggers SpringBoot jpa autoconfiguration.
If you want to use pure jdbc with spring boot, replace spring-boot-starter-data-jpa with spring-boot-starter-jdbc (https://mvnrepository.com/artifact/org.springframework.boot/)
In this case you need to
have mysql jdbc driver declared in maven dependencies
define spring.datasource.url, spring.datasource.username, spring.datasource.password in your properties or yaml (you don't need to define spring.datasource.driver if you have only one jdbc driver in your maven deps)
remove your DataSources configuration, since springboot will autoconfigure it for you

Related

Spring dblink and invoke trigger on another database

Hi there I would ask you about recommended solutions of connecting another database in Spring + Hibernate.
In my case I have small application with its database and what I need to do is to obtain some data from another (large) db.
Currently I'm doing that with postgresql and dblink but now it would be better to move this query into code.
In near future I'll also need to invoke triggers on that database.
So the question is whats the best practice to solve this kind of connection problems?
To sum up, what I need:
invoke another database trigger
invoke dblink and obtain data from another database
You can try this to invoke another database from your spring boot application. So your base application will use MySQL and the secondary database will be Postgres.
If you are using spring-boot then create lib folder under the project folder (Same hierarchy of src). Add required jdbc jar in that folder.
Then, create DataBaseConfig class with #Configuration, inside that class create ComboPooledDataSource and JdbcTemplate bean.
Now in your service class just do Autowiring of that JdbcTemplate bean. Then, write SQL query to get value using this method queryForList
In pom.xml, you can map that external jar file like below
<dependency>
<groupId>com.mysql</groupId>
<artifactId>db2jcc</artifactId>
<version>11.1</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/mysql.jar</systemPath>
</dependency>
#Configuration
public class DatabaseConfig {
#Bean(name = "employeeDataBase")
#ConfigurationProperties(prefix = "employee.datasource")
public ComboPooledDataSource employeeDataSource() {
return new ComboPooledDataSource();
}
#Bean(name = "employeeTemplate")
public JdbcTemplate employeeTemplate(#Qualifier("employeeDataBase") DataSource ds) {
return new JdbcTemplate(ds);
}
}
#Service
public class EmployeeService {
#Autowired
#Qualifier("employeeTemplate")
JdbcTemplate employeeTemplate;
public void getEmployeeFromExternalDB() {
List<Map<String, Object>> maps = employeeTemplate.queryForList("SELECT * FROM EMPLOYEE");
}
}
//application.properties
employee.datasource.jdbc-url=${EMPLOYEE_URL}
employee.datasource.user=${EMPLOYEE_UNAME}
employee.datasource.password=${EMPLOYEE_PWD}
employee.datasource.driver-class=com.edb.Driver
employee.datasource.max-idle-time=6
employee.datasource.min-pool-size=3
employee.datasource.max-pool-size=15
employee.datasource.jdbcUrl=${EMPLOYEE_URL}
employee.datasource.driver-class-name=com.edb.Driver

SpringBoot manage connection pool errors

I have a spring-boot application with 3 webservices that access to two different databases declared in application.properties file.
spring.datasource.url = jdbc:oracle
spring.datasource.username = aa
spring.datasource.password = aa
spring.seconddatasource.url = jdbc:oracle2
spring.seconddatasource.username = aa
spring.seconddatasource.password = aa
When I run the application, if a connection fails it ends the whole application even if one of the connections works.
I need to connect to all databases, and if a database isn't working, try to reconnect, but the application can not end.
I have tried these configurations but with no success :
testOnBorrow=true
validationQuery=SELECT 1
timeBetweenEvictionRunsMillis = 60000
Also I have a DataBaseConfig.java with
#Configuration
public class DataBaseConfig {
#Bean(name = "mysqlDb")
#Primary
#ConfigurationProperties(prefix="spring.datasource")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "sqliteDb")
#ConfigurationProperties(prefix="spring.secondDatasource")
public DataSource sqliteDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "cli")
public JdbcTemplate slaveJdbcTemplate(#Qualifier("mysqlDb") DataSource datasource) {
return new JdbcTemplate(datasource);
}
#Bean(name = "usr")
#Primary
public JdbcTemplate masterJdbcTemplate(#Qualifier("sqliteDb") DataSource secondDatasource) {
return new JdbcTemplate(secondDatasource);
}
}
Console errors :
Unable to create initial connections of pool
HHH000342: Could not obtain connection to query metadata : ORA-01034: ORACLE not available
ORA-27101: shared memory realm does not exist
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
Create the connection pools programmatically rather than letting Spring Boot autoconfigure them for you. Then you can handle any errors in building the datasource in your code. See:
Configure DataSource programmatically in Spring Boot
Alternatively create a single connection datasource at runtime rather than at boot time, and add retry logic in the event of an error (have a look at Spring Retry)

How to inject property values into Spring Boot component

In my Spring Boot Application, I implemented the following class with a method to call a stored procedure.
#Component
#ConfigurationProperties(prefix = "spring")
public class FmTrfUtil {
static int returnVal;
#Value("${spring.datasource.url}")
static String url;
public static int insertFmTrfs(List<String> trfs, String source) {
System.out.println(url);
EntityManager em = Persistence.createEntityManagerFactory("RIStore_FM").createEntityManager();
Session session = em.unwrap( Session.class );
final String[] trfArray = trfs.toArray(new String[trfs.size()]);
final String src = source;
session.doWork( new Work(){
public void execute(Connection conn) throws SQLException {
CallableStatement stmt = null;
OracleConnection oraCon = conn.unwrap(OracleConnection.class);
Array array = oraCon.createARRAY("VARCHAR2_TAB_T", trfArray);
stmt = conn.prepareCall("{? = call FM_TRF_UTIL.process_fm_trf(?,?)}");
stmt.registerOutParameter(1, Types.INTEGER);
stmt.setArray(2, array);
stmt.setString(3, src);
stmt.execute();
returnVal = stmt.getInt(1);
}
});
return returnVal;
}
}
Since calling stored procedure requires database connection, I need to load the these corresponding property values from application.properties:
spring.profiles.active=dev
spring.datasource.url=jdbc:oracle:thin:#ldap://xxx:389/risdev3, cn=OracleContext,dc=x,dc=net
spring.datasource.username=owner
spring.datasource.password=owner987
Based on the following articles about similar issue, Spring boot - custom variables in Application.properties and Using Spring-Boot configuration properties in your own classes and Spring Boot #ConfigurationProperties example, I added this annotation for my class #ConfigurationProperties(prefix = "spring") (properties for db connection all have "spring" as prefix). However when I run it with a test class as follows, I got error "the application must supply JDBC connection" which means properties in application.properties are not picked up.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = RistoreWebApplication.class, initializers = ConfigFileApplicationContextInitializer.class)
public class FmTrfUtilTest {
#Test
public void test() {
List<String> trfs = new ArrayList<String>();
trfs.add("TRF000001");
trfs.add("TRF000002");
int ret = FmTrfUtil.insertFmTrfs(trfs, "SARC");
assertTrue(ret > 0);
}
}
In order for #ConfigurationProperties to work, I added maven dependency spring-boot-configuration-processor too. Why is it still not working? What did I miss?
There are few things wrong here:
#Value does not work on static fields
#ConfigurationProperties are used to bind fields from application.properties or application.yml to Java object. Look at any #ConfigurationProperties annotated class from Spring Boot itself to easily understand how it should be used.
you should not use your own #ConfigurationProperties with prefix spring since it's already used by Spring Boot itself
spring-boot-configuration-processor is used only for nicer code completion in your IDE. You do not need this.
If you want to utilize Spring Boot configuration properties for database connection, instead of creating EntityManager like you do:
EntityManager em = Persistence.createEntityManagerFactory("RIStore_FM").createEntityManager();
You should just inject it assuming you have Spring Data JPA Starter in your dependency list.
I see you use lots of static methods and fields. That's not going to work with Spring. Use dependency injection instead and autowire what you need.

Flyway & MyBatis: Java Configuration for Spring-Boot

I'm trying to code a web-app using Spring-Boot, HSQLDB, Flyway and MyBatis. I started without MyBatis and Flyway happily created the database every time I had removed the HSQLDB.
After I added MyBatis all was fine until I had to make changes in the initial SQL-file and removed the database. Now I'm failing to start the web-app. It seems as if Flyway and MyBatis somehow depend on each other.
My database configuration:
#Configuration
public class DatabaseConfiguration {
#Bean
public DataSource dataSource() { ... }
#Bean
public DataSourceTransactionManager transactionManager() { ... }
#Bean
public static MapperScannerConfigurer mapperScannerConfigurer() { ... }
#Bean
public DataSourceInitializer dataSourceInitializer() throws Exception { ... }
#Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() throws Exception { ... }
#Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception { ... }
}
I fully understand that MapperScannerConfigurer, SqlSessionFactoryBean and SqlSessionTemplate are coming from MyBatis, all Flyway-stuff is done by Spring-Boot. From what I can tell Flyway needs a DataSource and MyBatis needs a database where the Flyway scripts ran already to allow the initialisation of the mappers.
The error I get is
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: user lacks privilege or object not found: MY_TABLE
### The error may exist in file [/home/work/Eclipse/com.sjngm.hs/target/classes/sqlmap/MyTableMapper.xml]
### The error may involve com.sjngm.hs.dao.mapper.MyTableMapper.getByName
### The error occurred while executing a query
### SQL: SELECT * FROM MY_TABLE WHERE Name = ?
### Cause: java.sql.SQLSyntaxErrorException: user lacks privilege or object not found: MY_TABLE
Note that there is no indication in the entire log-file that Flyway already did something. Also I can't find any CREATE TABLE in HSQLDB's files, which makes the above error saying that it can't find table MY_TABLE.
I already tried moving dataSource() to a new configuration class, but that didn't help.
What do I need to do to solve that indirect dependency?
What helped was making dataSource() a #FlywayDataSource.

Spring boot - Issue with multiple DataSource

I have a ReST service that needs to fetch data from two different DBs (Oracle and MySQL) and merge this data in the response.
I have below configuration.
Config for DB 1:
#Configuration
public class DbConfig_DB1{
#Bean(name="siebelDataSource")
public EmbeddedDatabase siebelDataSource(){
return new EmbeddedDatabaseBuilder().
setType(EmbeddedDatabaseType.H2).
addScript("schema.sql").
addScript("test-data.sql").
build();
}
#Autowired
#Qualifier("siebelDataSource")
#Bean(name = "siebelJdbcTemplate")
public JdbcTemplate siebelJdbcTemplate(DataSource siebelDataSource) {
return new JdbcTemplate(siebelDataSource);
}
}
Config for DB2:
#Configuration
public class DbConfig_DB2{
#Bean(name="brmDataSource")
public EmbeddedDatabase brmDataSource(){
return new EmbeddedDatabaseBuilder().
setType(EmbeddedDatabaseType.H2).
addScript("schema-1.sql").
addScript("test-data-1.sql").
build();
}
#Autowired
#Qualifier("brmDataSource")
#Bean(name = "brmJdbcTemplate")
public JdbcTemplate brmJdbcTemplate(DataSource brmDataSource) {
return new JdbcTemplate(brmDataSource);
}
}
Data Access:
#Repository
public class SiebelDataAccess {
protected final Logger log = LoggerFactory.getLogger(getClass());
#Autowired
#Qualifier("siebelJdbcTemplate")
protected JdbcTemplate jdbc;
public String getEmpName(Integer id) {
System.out.println(jdbc.queryForObject("select count(*) from employee", Integer.class));
Object[] parameters = new Object[] { id };
String name = jdbc.queryForObject(
"select name from employee where id = ?", parameters,
String.class);
return name;
}
}
I am not able to start the app as I below error:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private javax.sql.DataSource org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration.dataSource;
nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: brmDataSource,siebelDataSource
The issue is with two DataSource beans in the context. How to resolve this?
You could mark one of them as #Primary so Spring Boot auto configuration for transactions manager would know which one to pick. If you need to manage transactions with both of them then I'm afraid you would have to setup transactions management explicitly.
Please refer to the Spring Boot documentation
Creating more than one data source works the same as creating the first one. You might want to mark one of them as #Primary if you are using the default auto-configuration for JDBC or JPA (then that one will be picked up by any #Autowired injections).

Resources