I have a spring application and Multi-Node Server with Sticky Sessions. I want to keep the session in the database so as not to make replication sessions.
I tried to implement the jdbc session store using #EnableJdbcHttpSession but I did not manage to create a configuration so that the sessions in the database would work.
I have spring-session-jdbc in pom.xml:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-jdbc</artifactId>
<version>2.0.2.RELEASE</version>
<type>pom</type>
</dependency>
also have :
#Configuration
#EnableTransactionManagement
#EnableJdbcHttpSession
public class AppConfig{
#Autowired
private AppEnvironment env;
#Bean(name = "dataSource")
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getDriverClassName());
dataSource.setUrl(env.getDatabaseUrl());
dataSource.setUsername(env.getDatabaseUserName());
dataSource.setPassword(env.getDatabasePassword());
dataSource.setValidationQuery(env.getDatabaseValidationQuery());
dataSource.setTestOnBorrow(env.isTomcatTestOnBorrow());
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter jpaAdapter = new HibernateJpaVendorAdapter();
jpaAdapter.setShowSql(Boolean.valueOf(env.isJpaShowSql()));
jpaAdapter.setDatabase(Database.SQL_SERVER);
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceProvider(new HibernatePersistenceProvider());
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "com.humanbizz.web.entities" });
em.setPersistenceUnitName("persistenceUnit");
em.setJpaVendorAdapter(jpaAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", env.getHibernateDialect());
properties.setProperty("hibernate.default_catalog", env.getDefaultCatalog());
properties.setProperty("hibernate.hbm2ddl.auto", env.getHbm2ddlAuto());
properties.setProperty("hibernate.id.new_generator_mappings", String.valueOf(env.isIdNewGeneratorMappings()));
properties.setProperty("hibernate.default_schema", env.getDefaultSchema());
properties.setProperty("hibernate.connection.useUnicode", String.valueOf(env.isConnectionUseUnicode()));
properties.setProperty("hibernate.connection.charSet", env.getConnectionCharSet());
return properties;
}
#Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
My HttpSessionApplicationInitializer:
public class HttpSessionApplicationInitializer extends AbstractHttpSessionApplicationInitializer{
public HttpSessionApplicationInitializer() {
super(AppConfig.class);
}
#Override
public void onStartup(ServletContext container) {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(WebSecurity.class, Datasource.class);
container.addListener(new ContextLoaderListener(rootContext));
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
dispatcherContext.register(WebMvc.class);
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
container.addFilter("securityFilter", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(null, false, "/*");
}
}
I have a generated scheme at the database like here https://github.com/spring-projects/spring-session/blob/master/spring-session-jdbc/src/main/resources/org/springframework/session/jdbc/schema-sqlserver.sql
but when you start the application, the session is not stored in the table, I do not know if I missed anything in the configuration, everything I found that should be set up this one but it does not work, any ideas and help would welcome me
The main problem is that you have override the onStartup method of the AbstractHttpSessionApplicationInitializer with that you break the whole purpose of the AbstractHttpSessionApplicationInitializer which is to register the filter needed for Spring Session to do its work.
I would suggest using multiple WebApplicationInitializer so that you can take advantage of the defaults instead of reinventing the wheel. In your case that would mean 3 WebApplicationInitializer.
Bootstrap your application and servlet
Configure security
Configure Spring Session
To configure your application I suggest extending the AbstractAnnotationConfigDispatcherServletInitializer which basically does all you now do manually.
#Order(1)
public MainApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[] { AppConfig.class, WebSecurity.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebMvc.class };
}
}
Now that you have loaded all of your configuration you need to enable Spring Security for that create a class that extends AbstractSecurityWebApplicationInitializer.
#Order(2)
public SecurityInitializer extends AbstractSecurityWebApplicationInitializer() {}
This class is all you need as it will detect the configuration from the root context which is already loaded before hand.
Finally you want the Spring Session stuff and for that create a class extending AbstractHttpSessionApplicationInitializer.
#Order(3)
public SpringSessionInitializer extends AbstractHttpSessionApplicationInitializer {}
Again all you need as it will detect the filter in the earlier created configuration. You might want to place an #Order on each to make sure they get executed in the right order.
Related
I am working on a Spring-MVC application in which we are preparing to setup application on different servers. As each server can have it's own database related configuration, we are hoping to use an external properties file(outside the war file) which can be read while project is starting. How do I go about this approach?
For making it work, I have already moved application initialization code to Java, this way, static XML reading which we had before won't be required.
But, we are unsure how to create and add a properties file dynamically which atleast has these 3 values(JDBC URL containing DB name, username, password). All tables, other data will be created automatically.
Here is the class where app is initialized :
WebConfig.java :
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.ourapp.spring"})
#EnableTransactionManagement
#EnableCaching
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
#Bean
public ReloadableResourceBundleMessageSource messageSource(){
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("../resources/locale/messages.properties");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public LocaleChangeInterceptor localeInterceptor(){
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
interceptor.setParamName("lang");
return interceptor;
}
#Bean
public MappingJackson2HttpMessageConverter converter() {
return new MappingJackson2HttpMessageConverter();
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/img/**").addResourceLocations("/img/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
}
#Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("../webapp/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
#Bean
public DoNotTruncateMyUrls doNotTruncate(){
return new DoNotTruncateMyUrls();
}
#Bean
public MultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
#Bean
#Qualifier("primary_tx")
public HibernateTransactionManager getPrimaryTransactionManager() throws IOException {
HibernateTransactionManager txName= new HibernateTransactionManager();
txName.setSessionFactory(sessionFactory().getObject());
return txName;
}
#Bean
#Qualifier("extended_tx")
public HibernateTransactionManager txName() throws IOException {
HibernateTransactionManager txName= new HibernateTransactionManager();
txName.setSessionFactory(getExtendedSessionFactory().getObject());
return txName;
}
#Bean
#Qualifier("sessionFactory_origin")
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(new DataSourceConfig().primaryDataSource());
sessionFactory.setPackagesToScan("com.ourapp.spring");
return sessionFactory;
}
#Bean
#Qualifier("sessionFactory_extended")
public LocalSessionFactoryBean getExtendedSessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(new DataSourceConfig_Extended().secondaryDataSource());
sessionFactory.setPackagesToScan("com.ourapp.spring");
return sessionFactory;
}
}
Thank you. :-)
Probably what you are looking for is Profiles( #Profile or #Conditional )
Step1: Create a profile. The following is the example for prod profile. Similarly, you can create one for dev and qa
import javax.activation.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jndi.JndiObjectFactoryBean;
#Configuration
#Profile("prod")
#PropertySource("classpath:/com/<SomePath>/app.properties")
public class ProductionProfileConfig {
#Autowired Environment env;
#Bean
public DataSource dataSource() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName(env.getProperty("dbName"));
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndiObjectFactoryBean.getObject();
}
}
Step2 Activate profiles
Spring honors two separate properties when determining which profiles
are active: spring.profiles.active and spring.profiles.default. If
spring.profiles.active is set, then its value determines which
profiles are active. But if spring .profiles.active isn’t set, then
Spring looks to spring.profiles.default. If neither
spring.profiles.active nor spring.profiles.default is set, then there
are no active profiles, and only those beans that aren’t defined as
being in a profile are created
UPDATE
Use PropertyConfigurator.configure(Loader.getResource(<your-file-path>)); if the file is located outside the packaged war. Later you can simply inject values using #Value or SPel
Write a config manager, which will create configuration objects based on the name of properties file. For example,
Configuration config = ConfigurationManager.getConfig("dbConfig");
so, now your config object will contain all properties related to db. When you instantiate config object read all properties into this object.
Let's say your properties file contains following fields:
user.name = "Tom"
user.password = "pass"
Next time when you need "user.name", you would just do config.getString("user.name")
You can do this using PropertySourcesPlaceholderConfigurer
Create a bean like this
#Bean
public static PropertySourcesPlaceholderConfigurer devPropertyPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocations(new PathMatchingResourcePatternResolver().getResources("file:pathtToFile"));
configurer.setIgnoreUnresolvablePlaceholders(true);
return configurer;
}
For example if you write file:/tmp/dev/*.properties . It will load all the properties file under /tmp/dev.
If you want to customize it based on differnt environment(dev, testing, production). then you can use #Profile and create multiple beans.
#Bean
#Profile("dev")
public static PropertySourcesPlaceholderConfigurer devPropertyPlaceholderConfigurer() throws IOException {
..... // give dev properties file path
}
#Bean
#Profile("testing")
public static PropertySourcesPlaceholderConfigurer testPropertyPlaceholderConfigurer() throws IOException {
.....// give test properties file path
}
#Bean
#Profile("prod")
public static PropertySourcesPlaceholderConfigurer prodPropertyPlaceholderConfigurer() throws IOException {
.....// give prod properties file path
}
I'm feeling frustrated with my knowledge of spring and java, I hope someone can help me :)
I read the documentation of spring and searched for examples of how to implement multi tenancy in hanacloud platform but no one using spring data/eclipseLink ...
I really don't know how it is the life cycle of objects in framework and how to correctly configure in order to isolate the tenants in spring
The multi tenant strategy adopted was column descriptor to isolate the tenants, this column is described in superclass mapped in child entities...
Each consumer client is identified by url and can be accessed with this API, I've created a route to test the tenant id and the tenant id is different for each URl, which means the subscriptions to my applications its ok...
But the spring data don't isolate the data, i guess he is not creating the entity manager and repositories based in tenant id of consumers and create based in tenant id of provider, the ideia is create only one persistency unit and the data source separate the data based in the column descriptor,
The tenant's ids are generated by the system and its not possible create additional persistency units, they are created in the moment of subscription...
Here's the questions
About the LocalContainerEntityManagerFactoryBean, when he is called in stack ?
It's possible to create one entity manager for each tenant id based in request ?
my configuration class to set datasource
#Configuration
#Profile({ "neo" })
#EnableTransactionManagement
public class DataSourceNeoConfig {
#Bean
public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
final JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
DataSource dataSource = dataSourceLookup.getDataSource("java:comp/env/jdbc/DefaultDB");
return dataSource;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
EclipseLinkJpaVendorAdapter jpaVendorAdapter = new EclipseLinkJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setShowSql(true);
return jpaVendorAdapter;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
try {
lef.setDataSource(jndiDataSource());
} catch (IllegalArgumentException | NamingException e) {
e.printStackTrace();
}
lef.setJpaVendorAdapter(jpaVendorAdapter());
Properties properties = new Properties();
properties.setProperty("eclipselink.weaving", "false");
properties.setProperty(PersistenceUnitProperties.LOGGING_LEVEL, "FINE");
properties.setProperty("eclipselink.ddl-generation", "create-tables");
// eclipselink.tenant.id is column the identifier in mapped class
// #TenantDiscriminatorColumn(name = "TENANT_ID", contextProperty = "eclipselink.tenant.id", length = 36)
properties.setProperty("eclipselink.tenant.id", TenantHolder.getTenant());
lef.setPackagesToScan("com.strongit.models");
lef.setJpaProperties(properties);
lef.afterPropertiesSet();
return lef;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
}
The springBootApplication
#SpringBootApplication
#EnableJpaRepositories(basePackageClasses = AppNameApplication.class, entityManagerFactoryRef = "entityManagerFactory")
public class AppNameApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(AvaliacaoApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(AvaliacaoApplication.class);
}
}
Thanks in advance !!
I'm creating a standalone Java application that uses Spring Data with JPA.
Part of the class that creates the factory for the EntityManagerFactory is below:
#Configuration
#Lazy
public class JpaConfig {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(MultiTenantConnectionProvider connProvider, CurrentTenantIdentifierResolver tenantResolver) {
...
}
The problem is: I can only detect the Hibernate Dialect after the ApplicationContext is initialized, because this information is read from an external configuration service.
Since #Lazy did not work, is there any strategy to avoid creating this bean before it is used, i.e, only create it when another bean injects an instance of EntityManager?
I stumbled upon this issue recently and found a solution that worked. Unfortunately "container" managed beans will be initialized during startup and #Lazy is ignored even if the EntityManager is not injected anywhere.
I fixed it by using an in-memory H2 DB to construct the factory bean during startup and changed it later. I think here's what you can do for your issue.
pom.xml:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
</dependency>
Source code:
#Configuration
public class DataSourceConfig {
#Bean
public HikariDataSource realDataSource() {
...
}
#Bean
public DataSource localH2DataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
}
#Bean
public LocalContainerEntityManagerFactoryBean myEntityManagerFactory() throws PropertyVetoException {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(localH2DataSource());
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setShowSql(true);
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
return factoryBean;
}
}
#Component
#Lazy
public class Main {
#Autowired
private LocalContainerEntityManagerFactoryBean emf;
#Autowired
private HikariDataSource realDataSource;
#PostConstruct
private void updateHibernateDialect() {
// read the external config here
emf.setDataSource(realDataSource);
Properties jpaProperties = new Properties();
jpaProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.DB2Dialect");
factoryBean.setJpaProperties(jpaProperties);
}
}
I have configured and working Spring-based REST application, but now I'd like to convert it to Spring-Boot.
My application uses Spring-Data-JPA on top of JPA datasource with Hibernate provider:
#Configuration
#EnableJpaRepositories("foo.bar.web.repository")
#EnableTransactionManagement
public class RepositoryConfig {
// properties ommited
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(className);
dataSource.setUrl(url);
dataSource.setUsername(userName);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
factory.setPackagesToScan("foo.bar.web.domain");
factory.setDataSource(dataSource());
factory.setJpaPropertyMap(new HashMap<String, Object>() {{
put("hibernate.dialect", dialect);
put("hibernate.hbm2ddl.auto", hbm2ddl);
}});
factory.afterPropertiesSet();
return factory.getObject();
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory());
return transactionManager;
}
}
My REST endpoints implemented using SpringMVC with following configuration:
#Configuration
#EnableWebMvc
#ComponentScan("foo.bar.web.controller")
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public MultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
}
Web initializer:
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{
ApplicationConfig.class,
RepositoryConfig.class
};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{MvcConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
The problem is that I don't want to use Spring-Boot auto configuration because I'd like to reuse my existing configuration classes with minimal changes, but I cannot find correct way to do this. I tried to implement Spring-boot application class annotated with #SpringBootApplication, but I'm not 100% sure that my config classes is used, because in this case I get java.lang.ClassCastException: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean$$EnhancerBySpringCGLIB$$ba21071f cannot be cast to javax.persistence.EntityManagerFactory.
Also I tried throw away #EnableAutoConfiguration annotation from application class and add TomcatEmbeddedServletContainerFactory bean to my context manually, but in this case the embedded tomcat is not configured properly.
It would be great if somebody can give me a hint how to solve my problem. I believe that all I need to do is somehow replace my WebInitilizer with Spring-Boot config.
After spending a day in a research, I finally found a solition of my problem.
First of all I had to modify my entityManagerFactory() and transactionManager() beans:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
factory.setPackagesToScan("foo.bar.web.domain");
factory.setDataSource(dataSource());
factory.setJpaPropertyMap(new HashMap<String, Object>() {{
put("hibernate.dialect", dialect);
put("hibernate.hbm2ddl.auto", hbm2ddl);
}});
factory.afterPropertiesSet();
return factory;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
Also I totally removed my WebInitializerclass and removed #EnableWebMvc annotation from MvcConfig. In Spring-Boot it's not possible to have class extended from WebMvcConfigurerAdapter in classpath because if Spring-Boot find it, all automatic configuration related to SpringMVC will be skipped. Here is the final version of my MvcConfig class:
#Configuration
#ComponentScan("foo.bar.web.controller")
public class MvcConfig {
#Bean
public MultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
}
I used the version of Spring-Boot application class which shown in doc:
#SpringBootApplication(exclude = MultipartAutoConfiguration.class)
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
Note, that in my case I had to exclude MultipartAutoConfiguration from auto configuration because I've already have this feature configured in MvcConfig. Bun it is also possible to leave it autoconfigured, but in this case I had to tune allowed file size in application.properties config file or add a MultipartConfigElement bean to my classpath.
I've read I believe tried all of the posts on this, but no luck in finding the right answer.
I am using java based configuration with my spring mvc project, and wanted to try Spring CrudRepository, to get away from DAOs, and that is when the whole hell broke loose:
started with "no transaction is in progress" on flush after persist:
- tried adding #Transactional to the method - none of the variations found here worked
- tried changing configuration, but since it is java based, most of the answers are xml based. no luck either.
So finally I have to ask:
How to configure my project to make CrudRepository persist, or how to create Spring EntityManager using java configuration.
This is the last version of my configuration file:
#Configuration
#ComponentScan(basePackages = { "ba.fit.vms" })
#ImportResource(value = "classpath:spring-security-context.xml")
#EnableTransactionManagement
#EnableJpaRepositories
public class AppConfig {
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setLocation(new ClassPathResource("/persistence.properties"));
return ppc;
}
// Security Configuration
#Bean
public KorisnickiServis korisnickiServis(){
return new KorisnickiServis();
}
#Bean
public TokenBasedRememberMeServices rememberMeServices() {
return new TokenBasedRememberMeServices("remember-me-key", korisnickiServis());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new StandardPasswordEncoder();
}
// Jpa Configuration
#Value("${dataSource.driverClassName}")
private String driver;
#Value("${dataSource.url}")
private String url;
#Value("${dataSource.username}")
private String username;
#Value("${dataSource.password}")
private String password;
#Value("${hibernate.dialect}")
private String dialect;
#Value("${hibernate.hbm2ddl.auto}")
private String hbm2ddlAuto;
#Bean
public DataSource configureDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean configureEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(configureDataSource());
entityManagerFactoryBean.setPackagesToScan("ba.fit.vms");
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties jpaProperties = new Properties();
jpaProperties.put(org.hibernate.cfg.Environment.DIALECT, dialect);
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, hbm2ddlAuto);
//jpaProperties.put(org.hibernate.cfg.Environment.SHOW_SQL, true);
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Bean
public PlatformTransactionManager transactionManager() {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(configureEntityManagerFactory().getObject());
return transactionManager;
}
}
I've tried number of variations, but was always receiving same "no transaction is in progress" error.
Also, just a glimpse at the repos:
LokacijaRepository:
#Transactional
public interface LokacijaRepository extends CrudRepository<Lokacija, Long> {
}
And LokacijaRepositoryImpl:
#Repository
public class LokacijaRepositoryImpl implements LokacijaRepository {
protected static Logger logger = Logger.getLogger("repo");
#PersistenceContext // tried this as well(type= PersistenceContextType.EXTENDED)
private EntityManager entityManager;
#Override
#Transactional// tried number of variations here as well, like REQUIRED...
public <S extends Lokacija> S save(S entity) {
logger.debug("trying to save!");
try {
entityManager.persist(entity);
entityManager.flush();
return entity;
} catch (Exception e) {
logger.debug("error: "+ e.toString());
return null;
}
}
If you need anything else to help me figure this one out, let me know.
The problem is that you are attempting to create an implementation of LokacijaRepository (in LokacijaRepositoryImpl) while Spring Data JPA (which you have configured) is trying to do the same.
What you need to do is:
totally remove LokacijaRepositoryImpl
Either change configureEntityManagerFactory to entityManagerFactory or add entityManagerFactoryRef=configureEntityManagerFactory to #EnableJpaRepositories