I'm trying now to configure custom ConfigSource in my Quarkus App. Like in many other manuals i'm created my own DatabaseSourceConfig and implements org.eclipse.microprofile.config.spi.ConfigSource interface.I registered my ConfigSource in:
/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource
There is my ConfigSource:
public class DatabaseConfigSource implements ConfigSource {
private DataSource dataSource;
public DatabaseConfigSource() {
try {
dataSource = (DataSource) new InitialContext().lookup("openejb:Resource/config-source-database");
} catch (final NamingException e) {
throw new IllegalStateException(e);
}
}
#Override
public Map<String, String> getProperties() {
// Implementing Method
}
#Override
public String getValue(final String propertyName) {
// Implementing Method
}
#Override
public String getName() {
return DatabaseConfigSource.class.getSimpleName();
}
}
But this not working for Quarkus because of JNDI name. I need to use CDI. I was trying to use something like this:
#Inject
#io.quarkus.agroal.DataSource("my_connection")
AgroalDataSource usersDataSource;
and declare this connection in application.properties but it didn't help me. I'm getting all the time NULL Exception.
Maybe someone have ideas, how can i get DB connection there without to use JNDI namespace?
You can obtain the data source via
AgroalDataSource dataSource = Arc.container()
.instance(AgroalDataSource.class, new DataSource.DataSourceLiteral("my_connection"))
.get();
You'll need to do this somewhere else than the constructor though, I think, because the ConfigSource instance is created before CDI is fully booted. You can cache the obtained data source instance then to avoid executing this multiple times.
I found some answer myself, maybe it will be useful also for other ppl.
Like #Janmartiška said, CDI booted later, than ConfigSource, that's why i don't see any way to inject my connection via CDI.
I was created some HibernateUtil Class:
package org.myproject.config;
import java.util.Properties;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.myproject.entities.ConfigurationsEntity;
public class HibernateUtil {
private static SessionFactory sessionFactory;
private static SessionFactory buildSessionFactory() {
try {
Properties props = new Properties();
props.setProperty("hibernate.connection.url", "jdbc:mysql://[db-host]:[db-port]/db_name");
props.setProperty("hibernate.connection.driver_class", "com.mysql.cj.jdbc.Driver");
props.setProperty("hibernate.connection.username", "username");
props.setProperty("hibernate.connection.password", "password");
props.setProperty("hibernate.current_session_context_class", "thread");
props.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
Configuration configuration = new Configuration();
configuration.addProperties(props);
configuration.addAnnotatedClass(ConfigurationsEntity.class);
System.out.println("Hibernate Configuration loaded");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
System.out.println("Hibernate serviceRegistry created");
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
if(sessionFactory == null) sessionFactory = buildSessionFactory();
return sessionFactory;
}
}
than i used it in my SourceConfig:
package org.myproject.config;
import io.quarkus.runtime.annotations.RegisterForReflection;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.myproject.entities.ConfigurationsEntity;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
#RegisterForReflection
public class DatabaseSourceConfig implements ConfigSource {
public SessionFactory sessionFactory;
public Session currentSession;
public DatabaseSourceConfig() {
sessionFactory = HibernateUtil.getSessionFactory();
this.checkFactoryConnection();
}
public void checkFactoryConnection() {
if (currentSession == null || (currentSession != null && !currentSession.isOpen())) {
try {
currentSession = sessionFactory.getCurrentSession();
} catch (NullPointerException e) {
currentSession = sessionFactory.openSession();
}
}
}
#Override
public Map<String, String> getProperties() {
// Implementing Method
}
#Override
public String getValue(String propertyName) {
this.checkFactoryConnection();
ConfigurationsEntity conf = new ConfigurationsEntity();
currentSession.beginTransaction();
try {
Query query = currentSession.createNamedQuery("Configuration.selectOne", ConfigurationsEntity.class);
query.setParameter("name", propertyName);
conf = (ConfigurationsEntity) query.getSingleResult();
currentSession.getTransaction().commit();
} catch (Exception ex) {
currentSession.getTransaction().rollback();
}
return conf.getValue();
}
#Override
public String getName() {
return DatabaseSourceConfig.class.getSimpleName();
}
}
Now i can use my ConfigSource in other classes like:
#Inject
#ConfigProperty(name = "[property-name-like-in-db]")
public String someProperty;
After my further research it was found that ConfigSource has no access to CDi and application.properties. That is why there is nothing left but to establish a connection to the database in the manner described above.
However, I did a little editing of the example. I cached properties from the database and created a #ApplicationScoped Bean that looks into the database once every 5 minutes to see whether one of properties "updated_at" has a timestamp later than others from Bean loaded properties.
However, I have to say that according to Quarkus and Apache developers - this violates “immutable deployment” and is not planned to change the application settings during runtime. So it depends on you whether you write it in the app or not.
Related
I am facing some issues with while working with multiple datasources in Tomcat environment. Please find the details below.
I have below 2 datasources in my tomcat/conf/server.xml file
Data Source1:
<Resource name="myds1"
global="myds1"
auth="Container"
type="javax.sql.DataSource"
factory="com.tomcat.datasorceEncrypt.EncryptedDataSourceFactory"
driverClassName="oracle.jdbc.driver.OracleDriver"
singleton = "false"/>
Data Source2:
<Resource name="myds2"
global="myds2"
auth="Container"
type="javax.sql.DataSource"
factory="com.tomcat.datasorceEncrypt.EncryptedDataSourceFactory"
driverClassName="com.ibm.db2.jcc.DB2Driver"
singleton = "false"/>
Here is my EncryptedDataSourceFactory file which extends DataSourceFactory:
package com.tomcat.datasorceEncrypt;
import java.io.InputStream;
import java.util.Hashtable;
import java.util.Properties;
import java.util.stream.Stream;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.sql.DataSource;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.DataSourceFactory;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.apache.tomcat.jdbc.pool.XADataSource;
public class EncryptedDataSourceFactory extends DataSourceFactory {
private static final Log log = LogFactory.getLog(EncryptedDataSourceFactory.class);
private static final String PROP_DIALECT = "dialect";
private static final String[] CUSTOM_PROPERTIES = new String[]{PROP_DIALECT};
private static final String[] PROPERTIES = Stream.of(ALL_PROPERTIES, CUSTOM_PROPERTIES).flatMap(Stream::of).toArray(String[]::new);
#Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment)
throws Exception {
if (obj != null && obj instanceof Reference) {
Reference ref = (Reference) obj;
Properties properties = new Properties();
for (int i = 0; i < PROPERTIES.length; ++i) {
String propertyName = PROPERTIES[i];
RefAddr ra = ref.get(propertyName);
if (ra != null) {
String propertyValue = ra.getContent().toString();
properties.setProperty(propertyName, propertyValue);
}
}
return this.createDataSource(properties, nameCtx,false);
} else {
return null;
}
}
#Override
public DataSource createDataSource(Properties properties, Context context, boolean XA) throws Exception {
// Here we decrypt our password.
PoolConfiguration poolProperties = parsePoolProperties(properties);
Properties dbProperties = loadProperties();
poolProperties.setPassword(CryptoUtility.decryptDBPass(dbProperties.getProperty("DB_key"), dbProperties.getProperty("DB_password")));
poolProperties.setUsername(dbProperties.getProperty("DB_username"));
poolProperties.setUrl(dbProperties.getProperty("DB_url"));
System.out.println(poolProperties.getPoolName() + "****-------*****" + poolProperties.getName() );
System.out.println(poolProperties.getDataSourceJNDI() + "****------***" + poolProperties.getDataSource());
// The rest of the code is copied from Tomcat's DataSourceFactory.
if (poolProperties.getDataSourceJNDI() != null && poolProperties.getDataSource() == null) {
performJNDILookup(context, poolProperties);
}
org.apache.tomcat.jdbc.pool.DataSource dataSource = XA ? new XADataSource(poolProperties)
: new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
dataSource.createPool();
return dataSource;
}
private Properties loadProperties() {
Properties prop = new Properties();
try {
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("DBPassword.properties");
prop.load(inputStream);
}catch (Exception e) {
log.fatal("Error Loading the properties.", e);
throw new RuntimeException(e);
}
return prop;
}
}
I was trying to identify the datasource JNDI name from poolProperties.getDataSourceJNDI() so that I can apply proper credentials through my properties, but I am receiving poolProperties.getDataSourceJNDI() as null.
Have I missed any property in the Resource while creating datasource?
Note: While working with resource I could able to set user name and password though I have got poolProperties.getDataSourceJNDI() is null.
For timebeeing I have created one more class by extending DatasourceFactory for second datasource, it's working but I don't think it's an Idle solution.
In the routine, getObjectInstance, the parameter name can be used for this.
Based on the name, the decrypt method can be called for different data sources.
I had 8 datasources defined. I used this approach so that I just use one DataSourcefactory class.
These are my classes and configurations,
ServiceImpl.java
#Override
#Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public InsertUpdateSuccessBean insertNewSiteEntry(SiteEntry newSiteBean) throws SQLException {
dao.insert1();
dao.insert2();
except();
return new InsertUpdateSuccessBean(true,LoggerConstants.NEW_SITE_ENTRY_SUCCESS);
}
private void except()throws SQLException{
throw new SQLException();
}
DAOImpl.java
#Repository
public class DAOImpl implements DAO
{
#Autowired
private DataSourceTransactionManager transManager;
#Autowired
private CommonDAOUtils commonUtils;
private static final Logger logger = Logger.getLogger(HunterDAOImpl.class) ;
#Override
public Integer insert1() throws SQLException {
Integer insertNumRows = 0;
Connection connectionObject = transManager.getDataSource().getConnection();
PreparedStatement psObject = connectionObject.prepareStatement(
SQLQuery );
insertNumRows = psObject.executeUpdate();
commonUtils.closer(null,psObject,connectionObject);
// Function to close open connection resultsets statement objects
return insertNumRows;
}
#Override
public Integer insert2() throws SQLException {
Integer insertNumRows = 0;
Connection connectionObject = transManager.getDataSource().getConnection();
PreparedStatement psObject = connectionObject.prepareStatement(
SQLQuery );
insertNumRows = psObject.executeUpdate();
commonUtils.closer(null,psObject,connectionObject);
// Function to close open connection resultsets statement objects
return insertNumRows;
}
}
AppConfig.java
import com.interceptors.AppInterceptor;
import com.utils.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Component
#Configuration
#EnableWebMvc
#Configurable
#ComponentScan(basePackages = {Constants.BASE_PACKAGE})
#EnableAspectJAutoProxy(proxyTargetClass = true)
#PropertySource(Constants.DB_PROPERTIES_PATH)
#EnableTransactionManagement
public class AppConfig extends WebMvcConfigurerAdapter implements EnvironmentAware{
#Autowired
Environment environmentObject;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
PropertySourcesPlaceholderConfigurer placeHolder =new PropertySourcesPlaceholderConfigurer();
placeHolder.setLocation(new ClassPathResource(Constants.PROP_FILE_NAME));
return placeHolder;
}
#Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
internalResourceViewResolver.setPrefix(Constants.ROOT_PATH);
internalResourceViewResolver.setSuffix(Constants.JSP_DOT_EXTENSION);
return internalResourceViewResolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public DriverManagerDataSource dataSource(){
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName(environmentObject.getProperty(Constants.JDBC_DRIVERCLASS_NAME_PROP_FILE));
driverManagerDataSource.setUrl(environmentObject.getProperty(Constants.JDBC_URL_PROP_FILE));
driverManagerDataSource.setUsername(environmentObject.getProperty(Constants.JDBC_USERNAME_PROP_FILE));
driverManagerDataSource.setPassword(new String(Constants.PASSWORD));
return driverManagerDataSource;
}
#Bean
public AutowiredAnnotationBeanPostProcessor postProcessorBean(){
return new AutowiredAnnotationBeanPostProcessor();
}
private ClientHttpRequestFactory getClientHttpRequestFactory() {
int timeout = 5000;
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
clientHttpRequestFactory.setConnectTimeout(timeout);
return clientHttpRequestFactory;
}
#Bean
public DataSourceTransactionManager transactionManager(){
final DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource());
transactionManager.setRollbackOnCommitFailure(true);
return transactionManager;
}
}
And I also have an around advice for my project:
#Aspect
#Component
public class AroundAdvice
{
private static final Logger logger = Logger.getLogger(AroundAdvice.class) ;
// Add pointcuts here - package names here for around advice
#Around("execution(* com.beans.*.*(..)) || " +
"execution(* com.config.*.*(..)) || " +
"execution(* com.controller.*.*(..)) || " +
"execution(* com.dao.*.*(..)) || " +
"execution(* com.service.*.*(..)) || " +
"execution(* com.utils.*.*(..)) || "+
"execution(* com.interceptors.*.*(..))")
public Object aroundAdviceMethod(ProceedingJoinPoint proceedingObject)throws Throwable{
MethodSignature methodSignObject = (MethodSignature) proceedingObject.getSignature();
logger.debug(Constants.EXECUTING_METH_STR + methodSignObject.getMethod());
Object value = null;
try {
value = proceedingObject.proceed();
} catch (Exception e) {
e.printStackTrace();
logger.error(Constants.EXCEPTION_ASPECT_STR+methodSignObject.getMethod());
logger.error(Constants.EXCEPTION_MESSAGE,e);
throw e;
}
logger.debug(Constants.RETURN_STR+value);
return value;
}
}
On executing this flow, the inserts are successful, however when the exception is thrown, it is not rolling back. However, my logger reads that rolling back is initialized and done as follows,
14:11:51 DEBUG DataSourceTransactionManager:851 - Initiating transaction rollback
14:11:51 DEBUG DataSourceTransactionManager:325 - Rolling back JDBC transaction on Connection [org.postgresql.jdbc4.Jdbc4Connection#3b467b21]
14:11:51 DEBUG DataSourceTransactionManager:368 - Releasing JDBC Connection [org.postgresql.jdbc4.Jdbc4Connection#3b467b21] after transaction
Please let me know if I am missing something
The problem is your dao. You are opening connections yourself and therefor bypass all transaction management. Your dao is to complex just use a JdbcTemplate instead of your current code.
#Repository
public class DAOImpl implements DAO {
private static final Logger logger = Logger.getLogger(HunterDAOImpl.class) ;
private final JdbcTemplate jdbc;
public DAOImpl(DataSource dataSource) {
this.jdbc = new JdbcTemplate(dataSource);
}
#Override
public Integer insert1() throws SQLException {
return jdbc.update(SQLQuery);
}
#Override
public Integer insert2() throws SQLException {
return jdbc.update(SQLQuery);
}
}
This will do exactly the same as your code, with one main difference it will use the Connection opened when the transaction was started. Your sample used 3 separate connections and thus 3 individual transactions instead of 1 single transaction.
For teaching purposes, I am creating Hibernate console applications. Since Hibernate is quite complex, I want to be sure that I handle the initialization, exceptions, and shutdown correctly. The examples that I have found are not optimal in my opinion. (Official Hibernate examples use test cases.)
I have HibernateUtil for initialization and shutdown of Hibernate.
package com.zetcode.util;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
public class HibernateUtil {
private SessionFactory sessionFactory;
public HibernateUtil() {
createSessionFactory();
}
private void createSessionFactory() {
StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure() // configures settings from hibernate.cfg.xml
.build();
sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory();
// try {
// sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory();
// } catch (Exception e) {
// The registry would be destroyed by the SessionFactory, //but we had trouble building the SessionFactory
// so destroy it manually.
// StandardServiceRegistryBuilder.destroy(registry);
}
}
public SessionFactory getSessionFactory() {
// if (sessionFactory == null) {
// createSessionFactory();
// }
return sessionFactory;
}
public void shutdown() {
getSessionFactory().close();
}
}
Here is a console Java application:
package com.zetcode.main;
import com.zetcode.bean.Continent;
import com.zetcode.bean.Country;
import com.zetcode.util.HibernateUtil;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
public class Application {
public static void main(String[] args) {
HibernateUtil hutil = new HibernateUtil();
SessionFactory sessionFactory = hutil.getSessionFactory();
try (Session session = sessionFactory.openSession()) {
session.beginTransaction();
Continent europe = new Continent("Europe");
Continent asia = new Continent("Asia");
Country svk = new Country("Slovakia");
Country hun = new Country("Hungary");
Set<Country> europeCountries = new HashSet<>();
europeCountries.add(svk);
europeCountries.add(hun);
europe.setCountries(europeCountries);
Country chi = new Country("China");
Country afg = new Country("Afganistan");
Set<Country> asiaCountries = new HashSet<>();
asiaCountries.add(chi);
asiaCountries.add(afg);
asia.setCountries(asiaCountries);
session.save(europe);
session.save(asia);
// moved
//session.getTransaction().commit();
Query query = session.createQuery("SELECT c FROM Country c");
List<Country> countries = query.getResultList();
countries.stream().forEach((x) -> System.out.println(x));
Query query2 = session.createQuery("SELECT c FROM Continent c");
List<Continent> continents = query2.getResultList();
continents.stream().forEach((x) -> System.out.println(x));
session.getTransaction().commit();
} finally {
hutil.shutdown();
}
}
}
The example works OK. But, is this code correct? I am using Hibernate 5.2.
Edit I have incorporated the suggestions from the comments into the code.
I am using AbstractRoutingDataSource to create multitenancy in my application. I noticed that after a few redeployments of the webapp from my IDE I eventually get the MySQL error "Too many connections".
After further investigations, I found out that when I run the MySQL command show processlist;, I see that the open connection amount is increased by 10 after each deployment, which probably means that the connection pool is somehow still alive somewhere.
Before I used AbstractRoutingDataSource I used the default spring datasource configuration (using application.properties) and it worked fine.
Here's the Multitenant configuration class:
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
/**
* Created by Alon Segal on 16/03/2017.
*/
#Configuration
public class MultitenantConfiguration {
#Autowired
private DataSourceProperties properties;
/**
* Defines the data source for the application
*
* #return
*/
#Bean
#ConfigurationProperties(
prefix = "spring.datasource"
)
public DataSource dataSource() {
//Creating datasources map "resolvedDataSources" here
MultitenantDataSource dataSource = new MultitenantDataSource();
dataSource.setDefaultTargetDataSource(defaultDataSource());
dataSource.setTargetDataSources(resolvedDataSources);
// Call this to finalize the initialization of the data source.
dataSource.afterPropertiesSet();
return dataSource;
}
/**
* Creates the default data source for the application
*
* #return
*/
private DataSource defaultDataSource() {
.
.
.
}
}
And the datasource class:
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* Created by Alon Segal on 16/03/2017.
*/
public class MultitenantDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return TenantContext.getCurrentTenant();
}
}
I also tried to use #Bean(destroyMethod = "close") but there is not close method defined on AbstractRoutingDataSource.
I searched everywhere but could not find and answer. Can someone help me understand what's preventing the connection pool from being released between redeployments?
Thanks in advance.
Ok so I eventually solved the issue by giving up on using Spring's AbstractRoutingDataSource and instead using Hibernate's mechanism for multitenancy based on the solution that can be found in this article.
Long story short
You need to do 3 steps:
Step 1: Create a CurrentTenantIdentifierResolver
#Component
public class TenantIdentifierResolver implements CurrentTenantIdentifierResolver {
#Override
public String resolveCurrentTenantIdentifier() {
String tenantId = TenantContext.getCurrentTenant();
if (tenantId != null) {
return tenantId;
}
return DEFAULT_TENANT_ID;
}
#Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
Step 2: Create a MultiTenantConnectionProvider
#Component
public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider {
#Autowired
private DataSource dataSource;
#Override
public Connection getAnyConnection() throws SQLException {
return dataSource.getConnection();
}
#Override
public void releaseAnyConnection(Connection connection) throws SQLException {
connection.close();
}
#Override
public Connection getConnection(String tenantIdentifie) throws SQLException {
String tenantIdentifier = TenantContext.getCurrentTenant();
final Connection connection = getAnyConnection();
try {
if (tenantIdentifier != null) {
connection.createStatement().execute("USE " + tenantIdentifier);
} else {
connection.createStatement().execute("USE " + DEFAULT_TENANT_ID);
}
}
catch ( SQLException e ) {
throw new HibernateException(
"Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]",
e
);
}
return connection;
}
#Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
try {
connection.createStatement().execute( "USE " + DEFAULT_TENANT_ID );
}
catch ( SQLException e ) {
throw new HibernateException(
"Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]",
e
);
}
connection.close();
}
#SuppressWarnings("rawtypes")
#Override
public boolean isUnwrappableAs(Class unwrapType) {
return false;
}
#Override
public <T> T unwrap(Class<T> unwrapType) {
return null;
}
#Override
public boolean supportsAggressiveRelease() {
return true;
}
}
Step 3: Wire it up
#Configuration
public class HibernateConfig {
#Autowired
private JpaProperties jpaProperties;
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
MultiTenantConnectionProvider multiTenantConnectionProviderImpl,
CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) {
Map<String, Object> properties = new HashMap<>();
properties.putAll(jpaProperties.getHibernateProperties(dataSource));
properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProviderImpl);
properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolverImpl);
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("com.autorni");
em.setJpaVendorAdapter(jpaVendorAdapter());
em.setJpaPropertyMap(properties);
return em;
}
}
This is my first time using Hiberante.
I am trying to create a Hibernate session within my application using the following:
Session session = HiberanteUtil.getSessionFactory().openSession();
It gives me this error:
org.hibernate.HibernateException: /hibernate.cfg.xml not found
However I do not have a hibernate.cfg.xml file within my project.
How can I create a session without having this file?
import java.util.Properties;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import com.concretepage.persistence.User;
public class HibernateUtil {
private static final SessionFactory concreteSessionFactory;
static {
try {
Properties prop= new Properties();
prop.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/hibernate");
prop.setProperty("hibernate.connection.username", "root");
prop.setProperty("hibernate.connection.password", "");
prop.setProperty("dialect", "org.hibernate.dialect.MySQLDialect");
concreteSessionFactory = new AnnotationConfiguration()
.addPackage("com.concretepage.persistence")
.addProperties(prop)
.addAnnotatedClass(User.class)
.buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static Session getSession()
throws HibernateException {
return concreteSessionFactory.openSession();
}
public static void main(String... args){
Session session=getSession();
session.beginTransaction();
User user=(User)session.get(User.class, new Integer(1));
System.out.println(user.getName());
session.close();
}
}
The simples way to configure Hibernate 4 or Hibernate 5
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Hibernate reads a configuration from hibernate.cfg.xml and hibernate.properties.
You shouldn't call configure(), if you don't want to read hibernate.cfg.xml. With adding an annotated class
SessionFactory sessionFactory = new Configuration()
.addAnnotatedClass(User.class).buildSessionFactory();