I have a java project with spring framework. I have 4.3.12 (not spring boot!).
My repositories are interfaces, extended from spring base Repository interface.
I have implemented many unit tests with in-memory repositories, however in the database (postgreslq) I have some real database functions, which I would like to test. I cannot implement those with JPA queries.
Now I want to test those database functions, so I want to make integration test. However when the application context is initializing, i cannot load the repository interfaces, and receiving
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'com.company.model.UserRepository' available:
expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
exception.
Test class
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = { ApplicationContext.class })
public class UserControllerIntegrationTest {
#Autowired
private UserRepository userRepository;
.. test methods
}
The ApplicationContext.class is the real #Configuration class in the project. And it loads the datasource, entitymanagerfactory, transactionmanager, etc. when I execute the test.
package com.company.config;
import com.company.service.email.EmailService;
import com.zaxxer.hikari.HikariDataSource;
import java.util.Properties;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.flywaydb.core.Flyway;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.env.Environment;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
#Configuration
#PropertySources({
#PropertySource("classpath:application.properties"),
#PropertySource("file:${CCTNG_HOME}/application.properties")
})
#ComponentScan({"com.company"})
#EnableWebMvc
#EnableTransactionManagement
public class ApplicationContext {
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.url";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
private static final String PROPERTY_NAME_DATABASE_MIGRATE_USERNAME = "db.migrate.username";
private static final String PROPERTY_NAME_DATABASE_MIGRATE_PASSWORD = "db.migrate.password";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
private static final String PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY = "hibernate.ejb.naming_strategy";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_HIBERNATE_DDL_AUTO = "hibernate.hbm2ddl.auto";
// private static final String PROPERTY_NAME_HIBERNATE_IMPORT_FILES = "hibernate.hbm2ddl.import_files";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";
private static final String PROPERTY_NAME_LDAP_URL = "ldap.url";
private static final String PROPERTY_NAME_LDAP_USERNAME = "ldap.principal";
private static final String PROPERTY_NAME_LDAP_PASSWORD = "ldap.password";
private static final String PROPERTY_NAME_EMAIL_USERNAME = "email.username";
private static final String PROPERTY_NAME_EMAIL_APPPASSWORD = "email.apppassword";
#Resource
private Environment environment;
#Bean(initMethod = "migrate")
public Flyway flyway() {
Flyway flyway = new Flyway();
// flyway.setBaselineOnMigrate(true);
// flyway.setDataSource(dataSource());
flyway.setDataSource(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_URL), environment.getRequiredProperty(PROPERTY_NAME_DATABASE_MIGRATE_USERNAME),
environment.getRequiredProperty(PROPERTY_NAME_DATABASE_MIGRATE_PASSWORD), new String[]{});
return flyway;
}
// #Value("classpath:schema-postgresql.sql")
// private org.springframework.core.io.Resource schemaScript;
#Bean
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
dataSource.setJdbcUrl(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
dataSource.setUsername(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
dataSource.setPassword(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager() throws ClassNotFoundException {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());
return transactionManager;
}
#Bean(name = "entityManagerFactory")
#DependsOn("flyway")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() throws ClassNotFoundException {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPackagesToScan(environment.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
Properties jpaProterties = new Properties();
jpaProterties.put(PROPERTY_NAME_HIBERNATE_DIALECT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
jpaProterties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
jpaProterties.put(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY));
jpaProterties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
jpaProterties.put(PROPERTY_NAME_HIBERNATE_DDL_AUTO, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DDL_AUTO));
// jpaProterties.put(PROPERTY_NAME_HIBERNATE_IMPORT_FILES, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_IMPORT_FILES));
// jpaProterties.put("spring.jpa.properties.hibernate.jdbc.batch_size", "25");
// jpaProterties.put("spring.jpa.properties.hibernate.order_inserts", "true");
// jpaProterties.put("spring.jpa.properties.hibernate.order_updates", "true");
// jpaProterties.put("spring.jpa.properties.hibernate.generate_statistics", "true");
entityManagerFactoryBean.setJpaProperties(jpaProterties);
return entityManagerFactoryBean;
}
#Bean
public LdapContextSource ldapContextSource() {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(environment.getRequiredProperty(PROPERTY_NAME_LDAP_URL));
contextSource.setUserDn(environment.getRequiredProperty(PROPERTY_NAME_LDAP_USERNAME));
contextSource.setPassword(environment.getRequiredProperty(PROPERTY_NAME_LDAP_PASSWORD));
return contextSource;
}
#Bean
public LdapTemplate ldapTemplate() {
return new LdapTemplate(ldapContextSource());
}
#Bean(name = "emailService")
public EmailService email() {
Properties props = new Properties();
props.put("mail.smtp.host", "mailrelay.xxx.com");
props.put("mail.smtp.port", "25");
props.put("mail.smtp.auth", "false");
props.put("mail.smtp.connectiontimeout", "10000");
EmailService email = new EmailService(
environment.getRequiredProperty(PROPERTY_NAME_EMAIL_USERNAME),
environment.getRequiredProperty(PROPERTY_NAME_EMAIL_APPPASSWORD),
// environment.getRequiredProperty(PROPERTY_NAME_EMAIL_APPROOT),
props);
return email;
}
}
But for some reason the repositories are not initiated.
Thanks for any hint!
Related
I'm using SpringBoot 2.7.0 and trying to set an entity manager cause I have 2 databases.
But the entityManager that I instantiated doesn't work like default one.
It doesn't translate camel case to snake case for the properties of entity when it creates tables.
Even it doesn't follow the settings in application.yml. for example, spring.jpa.show-sql.
I configured it with below code.
#Slf4j
#RequiredArgsConstructor
#EnableJpaAuditing
#EnableJpaRepositories(basePackages = "com.xxx.yyy", entityManagerFactoryRef = "businessEntityManagerFactory", transactionManagerRef = "businessTransactionManager")
#EntityScan(basePackages = "com.xxx.yyy")
#Configuration
public class JpaConfiguration {
#Bean
public LocalContainerEntityManagerFactoryBean businessEntityManagerFactory(EntityManagerFactoryBuilder builder,
DataSource businessDataSource) {
return builder
.dataSource(businessDataSource)
.packages("com.xxx.yyy")
.build();
}
#Bean
public PlatformTransactionManager businessTransactionManager(LocalContainerEntityManagerFactoryBean businessEntityManagerFactory) {
return new JpaTransactionManager(Objects.requireNonNull(businessEntityManagerFactory.getObject()));
}
}
Does anyone know how I can instantiate an entity manager with same settings like spring boot default one?
Spring's convention-over-configuration does not work well with multiple database configurations.
Particuallary the configuration has to be autowired "manually". That's the reason springt.jpa.show-sql is not considered.
I will post an example on how to configure a database "manually".
import java.util.Map;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.zaxxer.hikari.HikariDataSource;
#Configuration
#EnableTransactionManagement
#EnableAutoConfiguration(exclude = {JdbcRepositoriesAutoConfiguration.class, JpaRepositoriesAutoConfiguration.class, DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, JdbcTemplateAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
#EnableJpaRepositories(basePackageClasses = {ExampleXyzRepository.class}, transactionManagerRef = DatabaseConfig.SPRING_TRANSACTION_MANAGER)
public class DatabaseConfig {
public static final String SPRING_TRANSACTION_MANAGER = "springTransactionManager";
private static LocalContainerEntityManagerFactoryBean createEntityManagerFactory(
final ObjectProvider<PersistenceUnitManager> persistenceUnitManagerProvider, final JpaProperties jpaProperties,
final HibernateProperties hibernateProperties, final DataSource dataSource, final Class<?> exampleEntityClass, final String persistenceUnit) {
return createEntityManagerFactoryBuilder(persistenceUnitManagerProvider.getIfAvailable(), jpaProperties, hibernateProperties)
.dataSource(dataSource)
.packages(exampleEntityClass)
.persistenceUnit(persistenceUnit)
.build();
}
private static EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(
final PersistenceUnitManager persistenceUnitManager, final JpaProperties jpaProperties, final HibernateProperties hibernateProperties) {
final JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(jpaProperties);
final Map<String, Object> expandedProperties = hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
return new EntityManagerFactoryBuilder(jpaVendorAdapter, expandedProperties, persistenceUnitManager);
}
private static JpaVendorAdapter createJpaVendorAdapter(final JpaProperties jpaProperties) {
final AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(jpaProperties.isShowSql());
adapter.setDatabasePlatform(jpaProperties.getDatabasePlatform());
adapter.setGenerateDdl(jpaProperties.isGenerateDdl());
if (jpaProperties.getDatabase() != null) {
adapter.setDatabase(jpaProperties.getDatabase());
}
return adapter;
}
private static HikariDataSource initializeDataSource(final DataSourceProperties dataSourceProperties) {
return dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
#Bean
#ConfigurationProperties("spring.datasource")
public DataSourceProperties springDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties("spring.datasource.hikari")
public DataSource springDataSource(#Qualifier("springDataSourceProperties") final DataSourceProperties springDataSourceProperties) {
return initializeDataSource(springDataSourceProperties);
}
#Bean
#ConfigurationProperties("spring.jpa")
public JpaProperties springJpaProperties() {
return new JpaProperties();
}
#Bean
#ConfigurationProperties("spring.jpa.hibernate")
public HibernateProperties springHibernateProperties() {
return new HibernateProperties();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
final ObjectProvider<PersistenceUnitManager> persistenceUnitManagerProvider,
#Qualifier("springJpaProperties") final JpaProperties springJpaProperties,
#Qualifier("springHibernateProperties") final HibernateProperties springHibernateProperties,
#Qualifier("springDataSource") final DataSource springDataSource) {
return createEntityManagerFactory(persistenceUnitManagerProvider, springJpaProperties, springHibernateProperties, springDataSource, ExampleXyzEntity.class, "default-persistence-unit");
}
#Bean(SPRING_TRANSACTION_MANAGER)
public JpaTransactionManager transactionManager(#Qualifier("entityManagerFactory") final EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
See how the configuration of DataSourceProperties, HibernateProperties, DataSource and JpaProperties are bound.
The second database is configured similar.
But:
use a different configuration prefix and not spring.jpa and spring.datasource - obviously
define the transactionManager in the org.springframework.transaction.annotation.Transactional while accessing the second database, e.g. in the CrudRepository
Sven's answer might be right but I didn't try it because I saw this after I found the solution and it looked requiring many changes my codes.
I figured this out with below codes
#Bean
public LocalContainerEntityManagerFactoryBean businessEntityManagerFactory(DataSource businessDataSource) {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(businessDataSource);
emf.setPackagesToScan("com.xxx.yyy");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(Boolean.valueOf(env.getProperty("spring.jpa.show-sql")));
emf.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect"));
properties.put("hibernate.format_sql", env.getProperty("spring.jpa.properties.hibernate.format_sql"));
properties.put("hibernate.hbm2ddl.auto", env.getProperty("spring.jpa.properties.hibernate.hbm2ddl.auto"));
properties.put("hibernate.physical_naming_strategy", "org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy");
properties.put("hibernate.implicit_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
emf.setJpaPropertyMap(properties);
return emf;
}
Note : the error goes away when i remove the beans for datasource and jdbctemplate. I did try the solution in https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-data-access
None of them worked. The properties are autowired in the correct fields when i remove the beans!
Here is applicaiton.properties:
app.datasource.url=jdbc:db2://stha7n0g7.iad.ca.inet:50001/icmnlsdb
app.datasource.username=<username>
app.datasource.password=<password>
app.datasource.driverClassName=com.ibm.db2.jcc.DB2Driver
Here is DB2Config:
package com.intactfc.soa.document.docpostmonitoring.configuration;
import java.lang.reflect.InvocationTargetException;
import java.sql.Driver;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.ClassUtils;
import com.intactfc.soa.document.docpostmonitoring.model.DB2ConfigModel;
#Configuration
public class DB2Configuration {
#Value("${app.datasource.username}")
private String username;
#Value("${app.datasource.password}")
private String password;
#Value("${app.datasource.driverClassName}")
private String driverClass;
#Value("${app.datasource.url}")
private String dataSourceURL;
#Autowired
DB2ConfigModel model;
//#Autowired
//Environment env;
//
// #Bean
// public DataSource getDataSource2()
// {
// System.out.println(model.getUrl());
// return DataSourceBuilder.create().build();
// }
#Bean
#Primary
public JdbcTemplate jdbcTemplate() throws IllegalAccessException, InvocationTargetException, InstantiationException {
// extract this 4 parameters using your own logic
final String driverClassName = driverClass;
final String jdbcUrl = dataSourceURL;
final String usernameL = username;
final String passwordL = password;
System.out.println("test"+driverClassName+jdbcUrl+usernameL+passwordL);
// Build dataSource manually:
final Class<?> driverClass = ClassUtils.resolveClassName(driverClassName, this.getClass().getClassLoader());
final Driver driver = (Driver) ClassUtils.getConstructorIfAvailable(driverClass).newInstance();
// final DataSource dataSource = new SimpleDriverDataSource(driver, jdbcUrl, username, password);
// or using DataSourceBuilder:
final DataSource dataSource = DataSourceBuilder.create().driverClassName(driverClassName).url(jdbcUrl).username(username).password(password).build();
// and make the jdbcTemplate
return new JdbcTemplate(dataSource);
}
}
As Spring document
the #Value annotation is performed by a BeanPostProcessor which in
turn means that you cannot use #Value within BeanPostProcessor or
BeanFactoryPostProcessor types
That means in method with #Bean, #Autowired annotations you can not get injected values. You got them after beans created only.
You can use Environment to get values from application.properties when initializing beans.
var username = environment.getProperty("app.datasource.username");
var password = environment.getProperty("app.datasource.password");
...
I'm trying to implement multi-tenancy architecture in my project. I am referring "http://anakiou.blogspot.com/2015/08/multi-tenant-application-with-spring.html" example. Everything is working fine in this example. But problem in this example is whenever new datasource to be add then we should add it manually. But i want something like adding datasource dynamically without application restart.
This is my application.properties
spring.jpa.hibernate.ddl-auto=none
spring.thymeleaf.cache=false
spring.datasource.datasource1.url=jdbc:mysql://localhost:3306/tenant_1
spring.datasource.datasource1.username=root
spring.datasource.datasource1.password=root
spring.datasource.datasource1.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.datasource2.url=jdbc:mysql://localhost:3306/tenant_2
spring.datasource.datasource2.username=root
spring.datasource.datasource2.password=root
spring.datasource.datasource2.driver-class-name=com.mysql.jdbc.Driver
This is DataSourceConfig.java
package com.anakiou.mt.config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
#Configuration
public class DataSourceConfig {
#Autowired
private MultitenancyProperties multitenancyProperties;
#Bean(name = { "dataSource", "dataSource1" })
#ConfigurationProperties(prefix = "spring.datasource.datasource1")
public DataSource dataSource1() {
DataSourceBuilder factory = DataSourceBuilder
.create(this.multitenancyProperties.getDatasource1().getClassLoader())
.driverClassName(this.multitenancyProperties.getDatasource1().getDriverClassName())
.username(this.multitenancyProperties.getDatasource1().getUsername())
.password(this.multitenancyProperties.getDatasource1().getPassword())
.url(this.multitenancyProperties.getDatasource1().getUrl());
return factory.build();
}
#Primary
#Bean(name = "dataSource2")
#ConfigurationProperties(prefix = "spring.datasource.datasource2")
public DataSource dataSource2() {
DataSourceBuilder factory = DataSourceBuilder
.create(this.multitenancyProperties.getDatasource2().getClassLoader())
.driverClassName(this.multitenancyProperties.getDatasource2().getDriverClassName())
.username(this.multitenancyProperties.getDatasource2().getUsername())
.password(this.multitenancyProperties.getDatasource2().getPassword())
.url(this.multitenancyProperties.getDatasource2().getUrl());
return factory.build();
}
}
MultitenancyProperties.java
package com.anakiou.mt.config;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
#ConfigurationProperties("spring.datasource")
public class MultitenancyProperties {
#NestedConfigurationProperty
private DataSourceProperties datasource1;
#NestedConfigurationProperty
private DataSourceProperties datasource2;
public DataSourceProperties getDatasource1() {
return datasource1;
}
public void setDatasource1(DataSourceProperties datasource1) {
this.datasource1 = datasource1;
}
public DataSourceProperties getDatasource2() {
return datasource2;
}
public void setDatasource2(DataSourceProperties datasource2) {
this.datasource2 = datasource2;
}
}
DataSourceBasedMultiTenantConnectionProviderImpl.java
package com.anakiou.mt.util;
import org.hibernate.engine.jdbc.connections.spi.AbstractDataSourceBasedMultiTenantConnectionProviderImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
#Component
public class DataSourceBasedMultiTenantConnectionProviderImpl extends
AbstractDataSourceBasedMultiTenantConnectionProviderImpl {
private static final long serialVersionUID = 8168907057647334460L;
private static final String DEFAULT_TENANT_ID = "tenant_1";
#Autowired
private DataSource dataSource1;
#Autowired
private DataSource dataSource2;
private Map<String, DataSource> map;
#PostConstruct
public void load() {
map = new HashMap<>();
map.put("tenant_1", dataSource1);
map.put("tenant_2", dataSource2);
}
#Override
protected DataSource selectAnyDataSource() {
return map.get(DEFAULT_TENANT_ID);
}
#Override
protected DataSource selectDataSource(String tenantIdentifier) {
return map.get(tenantIdentifier);
}
}
I am using the version of SpringBooot 1.8. Using #Cacheable annotations in the Service layer does not cache the data. Through debugging, it is found that some of the Service objects in the Controller layer are proxied by cglib, but some of them are not. The wordings are similar in nature. Why does this happen and how should it be solved ?
ApplicationBoot:
#SpringBootApplication
#EnableCaching
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}
Service:
#Service
#Slf4j
public class MovieServiceImpl extends ServiceImpl<MovieMapper,Movie> implements MovieService {
#Resource
private PlayService playService;
#Resource
private DownloadService downloadService;
#Resource
private PlayApiService playApiService;
#Resource
private AreaService areaService;
#Resource
private CategoryService categoryService;
#Resource
private IncrementRecordsService inService;
#Override
#Cacheable(value = "nowIsAndFuck")
public Movie nowIsAndFuck() {
log.debug("movie-test 执行查询:"+1);
return this.selectById(1);
}
}
Cache Config:
package com.aikanyingshi.web.core.config.cache;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
#Configuration
#Component
public class RedisConfig extends CachingConfigurerSupport {
#Resource
private RedisConfigProperties redisConfigProperties;
#Bean
public RedisConnectionFactory redisConnectionFactory(){
JedisConnectionFactory factory = new JedisConnectionFactory();
factory.setHostName(redisConfigProperties.getHost());
factory.setPort(redisConfigProperties.getPort());
if(redisConfigProperties.getPassword()!=null){
factory.setPassword(redisConfigProperties.getPassword());
}
return factory;
}
#Bean
public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String,String> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
template.afterPropertiesSet();
setSerializer(template);
return template;
}
#Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setDefaultExpiration(10);
return cacheManager;
}
private void setSerializer(RedisTemplate<String, String> template) {
Jackson2JsonRedisSerializer serializer
= new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(om);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
}
#Override
public KeyGenerator keyGenerator() {
return (o, method, objects) -> {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName());
sb.append(":");
sb.append(method);
for (Object object : objects) {
sb.append(":");
sb.append(object.toString());
}
return sb.toString();
};
}
}
I try to build web application on Java with Spring-MVC and Spring-Security.
In one module where I need get information from database, I can't get SessionFactory.
Form more information, here is my java config and other files source codes:
WebAppConfig.java:
package com.sprsec.init;
import org.hibernate.SessionFactory;
import org.springframework.context.annotation.*;
import org.springframework.context.support.AbstractMessageSource;
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.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.LocaleResolver;
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.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.view.JstlView;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Properties;
#Configuration
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan("com.sprsec")
#PropertySource("classpath:application.properties")
#ImportResource("classpath:spring-security.xml")
public class WebAppConfig extends WebMvcConfigurerAdapter {
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.url";
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";
private static final String PROPERTY_NAME_POOL_SIZE = "hibernate.pool_size";
#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;
}
#Bean
public SessionFactory getSessioFactory(){
return sessionFactory().getObject();
}
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));
properties.put(PROPERTY_NAME_POOL_SIZE, env.getRequiredProperty(PROPERTY_NAME_POOL_SIZE));
return properties;
}
#Bean
public HibernateTransactionManager transactionManager() {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
#Bean(name = "messageSource")
public AbstractMessageSource getMesageSource(){
return new DatabaseMessageSource(sessionFactory().getObject());
}
#Bean
public HandlerInterceptor localeChangeInterceptor(){
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
return localeChangeInterceptor;
}
#Bean
public UrlBasedViewResolver setupViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/pages/");
//resolver.setSuffix(".xhtml");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
registry.addInterceptor(localeChangeInterceptor);
}
#Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setDefaultLocale(StringUtils.parseLocaleString("en"));
return cookieLocaleResolver;
}
}
UserDAOImpl.java:
package com.sprsec.dao;
import com.sprsec.model.User;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
#Repository
public class UserDAOImpl implements UserDAO {
#Autowired
private SessionFactory sessionFactory;
private Session getSession(){
return sessionFactory.getCurrentSession();
}
public User getUser(String login) {
List<User> userList = new ArrayList<User>();
Query query = getSession().createQuery("from User u where u.login = :login");
query.setParameter("login", login);
System.out.println(query.getQueryString());
userList = query.list();
if (userList.size() > 0)
return userList.get(0);
else
return null;
}
public User getUserByLoginAndPassword(String login, String password){
Query query = getSession().createQuery("from User u where u.login = :login and u.password = :password");
query.setParameter("login", login);
query.setParameter("password", password);
User user = (User) query.uniqueResult();
return user;
}
}
Here, in UserDAOImpl.java, I retrieve an Exception. SessionFactory is null.
I mind what problem is in my WebAppConfig class because in DatabaseMessageSource.java I retrieve the same exception and I'l add this class in config and over constructor put SessionFactory. I do not want to add there every class that needs this dependency.
Error Message:
Caused by: java.lang.NullPointerException
at com.sprsec.init.DatabaseMessageSource.openSession(DatabaseMessageSource.java:95)
at com.sprsec.init.DatabaseMessageSource.getSQLDictionary(DatabaseMessageSource.java:104)
at com.sprsec.init.DatabaseMessageSource.loadTexts(DatabaseMessageSource.java:114)
at com.sprsec.init.DatabaseMessageSource.refreshProperties(DatabaseMessageSource.java:90)
at com.sprsec.init.DatabaseMessageSource.reload(DatabaseMessageSource.java:132)
at com.sprsec.init.DatabaseMessageSource.<init>(DatabaseMessageSource.java:35)
at com.sprsec.init.WebAppConfig.getMesageSource(WebAppConfig.java:90)
at com.sprsec.init.WebAppConfig$$EnhancerByCGLIB$$f4b941bb.CGLIB$getMesageSource$3(<generated>)
at com.sprsec.init.WebAppConfig$$EnhancerByCGLIB$$f4b941bb$$FastClassByCGLIB$$c1655200.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:286)
at com.sprsec.init.WebAppConfig$$EnhancerByCGLIB$$f4b941bb.getMesageSource(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:160)
... 57 more
DatabaseMessageSource.java:
public class DatabaseMessageSource extends AbstractMessageSource implements MessageSource {
private final Map<String, Map<String, String>> properties = new HashMap<String, Map<String, String>>();
#Autowired
private SessionFactory sessionFactory;
public DatabaseMessageSource() {
super();
//this.sessionFactory = sessionFactory;
this.setUseCodeAsDefaultMessage(true);
reload();
setParentMessageSource(this);
}
/**
* {#inheritDoc}
*/
#Override
protected String resolveCodeWithoutArguments(String code, Locale locale) {
return getText(code, locale);
}
/**
* {#inheritDoc}
*/
#Override
protected MessageFormat resolveCode(String code, Locale locale) {
String msg = getText(code, locale);
MessageFormat result = createMessageFormat(msg, locale);
return result;
}
/**
* #param code
* #param locale
* #return
*/
private String getText(String code, Locale locale) {
Map<String, String> localizedText = properties.get(locale.getLanguage());
System.out.println("===================================================");
System.out.println("requested code: "+code+" Result: "+localizedText.get(code));
System.out.println("===================================================");
String textForCurrentLanguage = null;
if (localizedText != null) {
textForCurrentLanguage = localizedText.get(code);
if (textForCurrentLanguage == null) {
saveKeyword(code);
reload();
return "__"+code;
}else if (textForCurrentLanguage.equals("")){
return "__"+code;
} else {
return textForCurrentLanguage;
}
} else {
return "__"+code;
}
}
/**
* #return properties which keeps localized text
*/
private Map<String, Map<String, String>> refreshProperties() {
properties.clear();
properties.putAll(loadTexts());
return properties;
}
public Session openSession(){
return sessionFactory.openSession();
}
private void saveKeyword(final String key){
Query query = openSession().createSQLQuery("insert into b_dictionary (keyword,lang_ru,lang_lv,lang_en) values ('"+key+"','','','')");
query.executeUpdate();
}
private Iterator getSQLDictionary(){
Query query = openSession().createSQLQuery("select m.keyword, m.lang_ru, m.lang_lv, m.lang_en from b_dictionary as m");
return query.list().iterator();
}
protected Map<String, Map<String, String>> loadTexts() {
Map<String, Map<String, String>> myProp = new HashMap<String, Map<String, String>>();
Map<String, String> dataRU = new HashMap<String, String>();
Map<String, String> dataLV = new HashMap<String, String>();
Map<String, String> dataEN = new HashMap<String, String>();
Iterator result = getSQLDictionary();
while( result.hasNext() ){
Object[] row = (Object[]) result.next();
String code = row[0].toString();
dataRU.put(code,row[1].toString());
dataLV.put(code,row[2].toString());
dataEN.put(code,row[3].toString());
}
myProp.put("ru", dataRU);
myProp.put("lv", dataLV);
myProp.put("en", dataEN);
return myProp;
}
/**
* Reload all codes.
*/
public void reload() {
refreshProperties();
}
}
Your problem is here:
new DatabaseMessageSource(sessionFactory().getObject());
Spring can only autowire beans that it manages; if you call new to create a bean yourself, #Autowired fields don't get filled in (and methods like #PostConstruct aren't called). The field should get filled in after it gets returned by the #Bean method, but if you need to access it in the bean's constructor, you'll have to pass it in as a parameter. The preferred solution is to move the code that relies on the #Autowired setup into a method annotated with #PostConstruct.
Try AnnotationSessionFactoryBean instead of LocalSessionFactoryBean
LocalSessionFactoryBean sessionFactoryBean = new AnnotationSessionFactoryBean();