Hikari + Hibernate + Postgres: can't connect - spring

I have an application that uses C3p0 with Hibernate 5 and Hibernate. I would like to try out Hikari, but I'm unable to get the application to run.
Maven
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.8</version>
</dependency>
Hibernate version is: 5.2.17.Final
Spring configuration
public LocalSessionFactoryBean sessionFactory()
throws Exception
{
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource( dataSource() );
...
}
private DataSource dataSource()
{
String jdbcUrl = "jdbc:postgresql://localhost/demo";
String user = "john";
String pw = "pwd123";
String url = String.format( "%s?user=%s&password=%s", jdbcUrl, user, pw);
final HikariDataSource ds = new HikariDataSource();
ds.setMaximumPoolSize( 10 );
ds.setDataSourceClassName( "org.postgresql.ds.PGSimpleDataSource" );
ds.addDataSourceProperty( "url", url );
...
return ds;
}
I have tried different permutations of the above approach, including passing username and password in to the Datasource directly:
ds.addDataSourceProperty( "user", "john" );
ds.addDataSourceProperty( "password", "pw123" );
But I always end up with this error:
Caused by: java.sql.SQLFeatureNotSupportedException
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:135)
which is caused by this Hikari function:
#Override
public Connection getConnection(String username, String password) throws SQLException
{
throw new SQLFeatureNotSupportedException();
}

Look at this issue. It tells you have to either use jdbcUrl or dataSourceClassName but not both. I see you are using both, so just use one as mentioned in that issue and check if that helps.
Apart from that, I just checked when that getConnection(String username, String password) throws SQLException gets called, looks like if you have:
hibernate.connection.username and/or hibernate.connection.password
set, thats when it gets called. Make sure you don't have those two properties set somewhere.

Related

Spring Boot Application using Oracle - ORA-01000: maximum open cursors exceeded while using Spring Jdbctemplate

I know there are lot of solution for this in internet but nothing seems to work for me.
I have following entries for in the pom.xml file for my jdk11 app
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
</parent>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>21.1.0.0</version>
</dependency>
<dependency>
<groupId>com.oracle.database.ha</groupId>
<artifactId>ons</artifactId>
<version>21.1.0.0</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ucp</artifactId>
<version>21.1.0.0</version>
</dependency>
I am using connection pool with config as follows
platform: oracle
url: jdbc:oracle:thin:#localhost:1521:TEST
connectionFactoryClassName: oracle.jdbc.pool.OracleDataSource
fastConnectionFailoverEnabled: true
username: test
password: test
initialPoolSize: 3
minPoolSize: 0
maxPoolSize: 12
inactivityTimeout: 300
queryTimeout: 600
validateConnectionOnBorrow: true
I am only querying for table no add or update to oracle record, something like this
PoolDataSource dataSource = PoolDataSourceFactory.getPoolDataSource();
#Bean
public DaoSpringJdbcImpl listenerDAO(final DataSource dataSource ) {
NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(dataSource );
DaoSpringJdbcImpl listenerDAO = new DaoSpringJdbcImpl(template);
return listenerDAO;
}
public class DaoSpringJdbcImpl {
private NamedParameterJdbcTemplate jdbcTemplate;
public DaoSpringJdbcImpl(NamedParameterJdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void method() {
List<String> results = jdbcTemplate.query(SQL_QUERY, params,
new RowMapper<String>()
{
public String mapRow(ResultSet rs, int rowNum) throws SQLException
{ return rs.getString(0)}
}
}
so everytime my application queries it opens up new cursor and never close it ultimately resulting to open cursor exception
PS I tried adding env property spring.jdbc.getParameterType.ignore = true which didnt work

How to authenticate to LDAP using Spring LDAP

I'm kind of newbie on Spring Boot and have a Jndi code that authenticates to an LDAP server just fine. But now i want to migrate my code to Spring LDAP, but get [LDAP: error code 49 - 80090308: LdapErr: DSID-0C09044E, comment: AcceptSecurityContext error, data 52e, v2580] every time.
So my JNDI code looks something like this:
public void connect(String userName, String pwd) throws NamingException, IllegalStateException {
Hashtable<String, String> env = new Hashtable<String, String>();
try {
env.put(LdapContext.CONTROL_FACTORIES, "com.sun.jndi.ldap.ControlFactory");
env.put(Context.SECURITY_PRINCIPAL, userName+"#domain.net);
env.put(Context.SECURITY_CREDENTIALS, pwd);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://server:389");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
context = new InitialDirContext(env);
} finally {
if (context !=null) {
log.info("Authentication successfull");
try {
context.close();
context = null;
} catch(NamingException ne) {
log.error(ne.getMessage());
ne.printStackTrace();
}
} else {
throw new IllegalStateException("Can't obtain LDAP context");
}
}
}
Very straightforward.
So far i have configured in my Spring boot application.properties file, the following:
spring.ldap.urls=Ldap://server:389
spring.ldap.embedded.credential.username=cn=ceadministrator
spring.ldap.embedded.credential.password=******
spring.ldap.embedded.base-dn=dc=domain,dc=net
Letting Spring Ldap manage the connection and initialization
Implemented this in order to search for some user:
public List<User> getUser(String userName) throws NamingException, LDAPException {
LdapQuery query = LdapQueryBuilder.query()
.searchScope(SearchScope.SUBTREE)
.timeLimit(3000)
.countLimit(10)
.attributes("cn")
.base(ldapConfig.getBase())
.where("objectClass").is("user")
.and("sAMAccountName").is(userName);
log.info("ldapTemplate: "+ldapTemplate);
return ldapTemplate.search(query, new UserAttributesMapper());
}
private class UserAttributesMapper implements AttributesMapper<User> {
#Override
public User mapFromAttributes(Attributes attributes) throws NamingException {
User user = new User();
if (attributes == null) {
log.warn("atttrs null");
return user;
}
user.setFirstName((String) attributes.get("cn").get());
Attribute sn = attributes.get("sAMAccountName");
if (sn != null) {
user.setUserName((String) sn.get());
}
return user;
}
}
But it throws an AutheticationException:
javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C09044E, comment: AcceptSecurityContext error, data 52e, v2580
What am i missing/doing wrong?... Please help
Well finally i got it.
I discovered that SpringBoot offers an embedded LDAP server, inserting the
"unboundid-ldapsdk" dependency into your pom.xml file. This is no my desired scenario, because i already had a production environment LDAP server to connect to.
So i simply needed to configure the following properties in my application.properties file like this:
spring.ldap.urls=Ldap://192.17.1.4:389
spring.ldap.base=ou=Organization,OU=Some users unit,dc=depr,dc=net
spring.ldap.username=administrator
spring.ldap.password=SomePassword
Understanding that the "Spring.ldap.base" is the base where my searches are going to start and has nothing to do with my administrator credentials.

Hibernate 4 -> 5 upgrade leads to LazyInitializationException

I upgraded a project from Spring 4.3.9.RELEASE + Hibernate 4.3.11.Final to Spring Boot 2.1.4.RELEASE and Hibernate 5.3.9.Final. The queries are still working fine, but I'm getting LazyInitializationException with some #OneToMany class members.
First I retrieve the object, which has a reference to a #OneToMany List, from the #Transaction service. The collection is returned to the controller, and from there it goes back to Spring to be serialized into a json. The controller has #RestController, so it knows what to do.
In Spring 4.3.9.RELEASE + Hibernate 4.3.11.Final everything was fine, even though OpenEntityManagerInView wasn't enabled by configuration and the collection wasn't loaded with EAGER mode. But in Spring Boot 2.1.4.RELEASE and Hibernate 5.3.9.Final the same thing doesn't work anymore. I've tried enabling OEMIV, by setting spring.jpa.open-in-view=true, but even this doesn't seem to work or it's being overriden somewhere.
If I enable EAGER loading mode for that collection, everything works fine.
pom.xml
<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>
#Entity
#JsonSerialize(using = TemplateSerializer.class)
public class Template implements Serializable {
#Id
#GeneratedValue
private Long id;
private String name;
#ManyToOne
private ObjFormat objFormat;
#OneToOne
#JoinColumn(name = "event_id")
#OnDelete(action = OnDeleteAction.CASCADE)
private Event event;
#OneToMany
#JoinColumn(name = "category_id")
private List<Category> linkToCategories;
The problem is caused by field linkToCategories. If I configure #OneToMany(fetch = FetchType.EAGER) everything works fine.
Application configuration:
#Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource) throws ClassNotFoundException {
LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
localSessionFactoryBean.setDataSource(dataSource);
localSessionFactoryBean.setPackagesToScan("com.project.backend.model",
"com.project.backend.hibernate.converters");
return localSessionFactoryBean;
}
#Bean
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
Later edit:
After a lot of debugging, the difference between the old and the new Hibernate functionality is in the HibernateTransactionManager. In the method doGetTransaction(), in Hibernate 4 it finds the SessionHolder object when calling
TransactionSynchronizationManager.getResource(getSessionFactory())
while in Hibernate 5 it doesn't.
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
if (sessionHolder != null) {
if (logger.isDebugEnabled()) {
logger.debug("Found thread-bound Session [" + sessionHolder.getSession() + "] for Hibernate transaction");
}
txObject.setSessionHolder(sessionHolder);
}
else if (this.hibernateManagedSession) {
try {
Session session = this.sessionFactory.getCurrentSession();
if (logger.isDebugEnabled()) {
logger.debug("Found Hibernate-managed Session [" + session + "] for Spring-managed transaction");
}
txObject.setExistingSession(session);
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException(
"Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);
}
}
In the method doBegin, a new session is created and set on the txObject for every request.
if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
Interceptor entityInterceptor = getEntityInterceptor();
Session newSession = (entityInterceptor != null ?
getSessionFactory().withOptions().interceptor(entityInterceptor).openSession() :
getSessionFactory().openSession());
if (logger.isDebugEnabled()) {
logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
}
txObject.setSession(newSession);
}
My experience with Hibernate is fairly small, so here I'm stuck. It's probably a configuration thing, but I can't find it.
As M. Deinum was saying, the Spring 4.3.9.RELEASE + Hibernate 4.3.11.Final configuration was loading OpenSessionInViewFilter, which explains why all the queries were going through successfully. After configuring the same filter in Spring Boot, everything is back to normal. Add the following bean to register the filter:
#Bean
public FilterRegistrationBean<OpenSessionInViewFilter> registerOpenSessionInViewFilterBean() {
FilterRegistrationBean<OpenSessionInViewFilter> registrationBean = new FilterRegistrationBean<>();
OpenSessionInViewFilter filter = new OpenSessionInViewFilter();
registrationBean.setFilter(filter);
return registrationBean;
}
The next step is to replace plain Hibernate with JPA, and OpenSessionInViewFilter with OpenEntityManagerInViewFilter.
Thanks M. Deinum.
#xxxToMany annotations inicates that fetch type is LAZY by default. It means that you need to initialize collection your entity refers to.
Eg.
#Entity
public class Book {
#OneToMany
public List<Author> authors;
}
There is few ways to resolve this. You can modify #OneToMany annotation with:
#OneToMany(FetcType=FetchType.EAGER)
Or to make a method where you will initialize authors eg.:
public void initializeAuthors(Book book) {
Book b = em.find(Book.class, book.getId());
List<Author> authors = new ArrayList<>(b.getAuthors());
book.setAuthors(authors);
}
If you have #NamedQueries on your entities, you can do that by adding LEFT JOIN FETCH on your collections.

Spring-Boot Elasticseach EntityMapper can not be autowired

Based on this answer and the comments I implemented the code to receive the scores of an elastic search query.
public class CustomizedHotelRepositoryImpl implements CustomizedHotelRepository {
private final ElasticsearchTemplate elasticsearchTemplate;
#Autowired
public CustomizedHotelRepositoryImpl(ElasticsearchTemplate elasticsearchTemplate) {
super();
this.elasticsearchTemplate = elasticsearchTemplate;
}
#Override
public Page<Hotel> findHotelsAndScoreByName(String name) {
QueryBuilder queryBuilder = QueryBuilders.boolQuery()
.should(QueryBuilders.queryStringQuery(name).lenient(true).defaultOperator(Operator.OR).field("name"));
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder().withQuery(queryBuilder)
.withPageable(PageRequest.of(0, 100)).build();
DefaultEntityMapper mapper = new DefaultEntityMapper();
ResultsExtractor<Page<Hotel>> rs = new ResultsExtractor<Page<Hotel>>() {
#Override
public Page<Hotel> extract(SearchResponse response) {
ArrayList<Hotel> hotels = new ArrayList<>();
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
try {
Hotel hotel = mapper.mapToObject(hit.getSourceAsString(), Hotel.class);
hotel.setScore(hit.getScore());
hotels.add(hotel);
} catch (IOException e) {
e.printStackTrace();
}
}
return new PageImpl<>(hotels, PageRequest.of(0, 100), response.getHits().getTotalHits());
}
};
return elasticsearchTemplate.query(nativeSearchQuery, rs);
}
}
As you can see I needed to create a new instance of DefaultEntityMapper mapper = new DefaultEntityMapper(); which should not be the case because it should be possible to #Autowire EntityMapper. If I do so, I get the exception that there is no bean.
Description:
Field entityMapper in com.example.elasticsearch5.es.cluster.repository.impl.CustomizedCluserRepositoryImpl required a bean of type 'org.springframework.data.elasticsearch.core.EntityMapper' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.data.elasticsearch.core.EntityMapper' in your configuration.
So does anybody know if its possible to autowire EntityMapper directly or does it needs to create the bean manually using #Bean annotation.
I use spring-data-elasticsearch-3.0.2.RELEASE.jar where the core package is inside.
My pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
</dependency>
I checked out the source code of spring-data-elasticsearch. There is no bean/comoponent definition for EntityMapper. It seems this answer is wrong. I test it on my project and get the same error.
Consider defining a bean of type 'org.springframework.data.elasticsearch.core.EntityMapper' in your configuration.
I couldn't find any other option by except defining a #Bean

Spring boot - C3P0 connection pooling with Spring Data

I have a spring boot(1.4.3.RELEASE) application with MySQL as a backend. Using Spring Data with Oracle UCP and MySQL Java connection for connection pooling. Things are working fine.
For, UCP connection pooling, I create a data source with the below code.
#Bean(name = "appDataSource")
#Primary
public PoolDataSource appDataSource() {
this.pooledDataSource = PoolDataSourceFactory.getPoolDataSource();
final String databaseDriver = environment.getRequiredProperty("application.datasource.driverClassName");
final String databaseUrl = environment.getRequiredProperty("application.datasource.url");
final String databaseUsername = environment.getRequiredProperty("application.datasource.username");
final String databasePassword = environment.getRequiredProperty("application.datasource.password");
final String initialSize = environment.getRequiredProperty("application.datasource.initialSize");
final String maxPoolSize = environment.getRequiredProperty("application.datasource.maxPoolSize");
final String minPoolSize = environment.getRequiredProperty("application.datasource.minPoolSize");
// final String poolName =
// environment.getRequiredProperty("application.datasource.poolName");
try {
pooledDataSource.setConnectionFactoryClassName(databaseDriver);
pooledDataSource.setURL(databaseUrl);
pooledDataSource.setUser(databaseUsername);
pooledDataSource.setPassword(databasePassword);
pooledDataSource.setInitialPoolSize(Integer.parseInt(initialSize));
pooledDataSource.setMaxPoolSize(Integer.parseInt(maxPoolSize));
pooledDataSource.setMinPoolSize(Integer.parseInt(minPoolSize));
pooledDataSource.setSQLForValidateConnection(TEST_SQL);
pooledDataSource.setValidateConnectionOnBorrow(Boolean.TRUE);
pooledDataSource.setConnectionWaitTimeout(CONNECTION_WAIT_TIMEOUT_SECS);
// pooledDataSource.setConnectionPoolName(poolName);
}
catch (final NumberFormatException e) {
LOGGER.error("Unable to parse passed numeric value", e);
}
catch (final SQLException e) {
LOGGER.error("exception creating data pool", e);
}
LOGGER.info("Setting up datasource for user:{} and databaseUrl:{}", databaseUsername, databaseUrl);
return this.pooledDataSource;
}
Now, I am trying to move to C3P0 based connection pooling and hence added the below property in my application.properties
Configure the C3P0 database connection pooling module
spring.jpa.properties.hibernate.c3p0.max_size = 15
spring.jpa.properties.hibernate.c3p0.min_size = 6
spring.jpa.properties.hibernate.c3p0.timeout = 2500
spring.jpa.properties.hibernate.c3p0.max_statements_per_connection = 10
spring.jpa.properties.hibernate.c3p0.idle_test_period = 3000
spring.jpa.properties.hibernate.c3p0.acquire_increment = 3
spring.jpa.properties.hibernate.c3p0.validate = false
spring.jpa.properties.hibernate.c3p0.numHelperThreads = 15
and injecting Datasource a below.
#Bean
public ComboPooledDataSource dataSource() {
return new ComboPooledDataSource();
}
But, it is not working. Is there anything I am missing here.
Hope, C3P0 is better than UCP.
Thanks,

Resources