Reputation:
This is not about Spring Boot at all.
My English could be better.
Using below Config for Spring Data I'm trying to execute DML requests.
Exactly CrudRepository#save
method.
However executing Spring's CrudRepository#save method I'm getting next:
hibernate.show_sql
feature.====================================================
Not sure but it looks like a Transaction issue.
Seems that there is no transaction at that point,
so out of transaction CRUD Repos is not able to execute DML requests,
including CrudRepository#save
.
Maybe it is something wrong with configuration? Have a look please and feel free to ask for any additional info.
UPDATE: The next bad-practice workaround helped me to reach the "Update" statements execution.
//(autowired, shared entity manager)
entityManager.joinTransaction();
repository.save(user);
However it is still a bad practice approach. In this case Spring's purpose is lost. Anyway it is required for me to use Declarative Code-based Transaction managment. Question is still open: What is wrong with my config? @Transactional annotation still doesn't work
User domain entity:
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@Entity
@Table(name = "users")
public class User
{
@Id
@Column(name = "id_pk", length = 11)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int idPk;
@Column(name = "user_id", length = 25, nullable = false, unique = true)
private String userId;
@Column(name = "email_addr", length = 120)
private String email;
}
Domain-specific Spring Data CRUD Repository declaration:
public interface UserRepository extends CrudRepository<User, Integer> {
//nothing specific
}
Spring (Boot-less) Code-based configuration:
@EnableJpaRepositories(basePackages = "***",
transactionManagerRef = "jpaTransactionManager")
@EnableTransactionManagement
public class DataConfig
{
@Bean
public EntityManagerFactory entityManagerFactory()
{
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource());
factory.setPackagesToScan(DOMAIN_ENTITY_SCAN_PACKAGE);
factory.setJpaVendorAdapter(getVendorAdapter());
factory.afterPropertiesSet();
return factory.getObject();
}
private HibernateJpaVendorAdapter getVendorAdapter()
{
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(Boolean.TRUE);
return vendorAdapter;
}
@Bean
public JpaTransactionManager jpaTransactionManager()
{
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory());
txManager.afterPropertiesSet();
return txManager;
}
}
Upvotes: 6
Views: 12484
Reputation:
Finally, I've found a solution for my case.
Since I'm using Spring without its Boot part I had to configure custom WebApplicationInitializer to let Spring manage application entry point:
public class MainWebAppInitializer implements WebApplicationInitializer
{
@Override
public void onStartup(final ServletContext sc)
{
AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
root.register(WebAppConfiguration.class, DataConfig.class);
sc.addListener(new ContextLoaderListener(root));
...other not related code ommited
}
}
So because I've registered both Config Classes (WebAppConfiguration.class, DataConfig.class) using AnnotationConfigWebApplicationContext#register I thought annotating configs with @Configuration would be Redundand.
And I was wrong at that point.
To register TransactionManager correctly you SHOULD annotate your Jpa Config class with @Configuration.
Thus I've managed to annotate my config classes with @Configuration and this solved my issue.
Now Spring CRUD Repositories are able to run DML queries to DB (with help of #save methods). Precisely talking: now Repositories are able to open own transactions and run required queries in terms of these transactions.
Upvotes: 2
Reputation: 1628
I didnot get this, how you are initializing the datasource properties ? I could not see in your given code.
factory.setDataSource(dataSource());
You should be designing you Configuration class like below. Use both the :
entityManagerFactoryRef="entityManagerFactory",
transactionManagerRef="jpaTransactionManager"
Read the hibernate properties from a yaml or properties file.
And set your datasource
Configuration class : DataConfig
/**
* @author Som
*
*/
@Configuration
@EnableJpaRepositories(basePackages="package-name",
entityManagerFactoryRef="entityManagerFactory",
transactionManagerRef="jpaTransactionManager")
@EnableTransactionManagement
@PropertySource(value = { "classpath:application.yml" })
public class DataConfig {
@Autowired
private Environment environment;
@Value("${datasource.myapp.maxPoolSize:10}")
private int maxPoolSize;
/**
* Populate DataSourceProperties object directly from application.yml
* *
*/
@Bean
@Primary
@ConfigurationProperties(prefix = "datasource.myapp")
public DataSourceProperties dataSourceProperties(){
return new DataSourceProperties();
}
/**
* Configure HikariCP pooled DataSource.
*
*/
@Bean
public DataSource dataSource() {
DataSourceProperties dataSourceProperties = dataSourceProperties();
HikariDataSource dataSource = (HikariDataSource) DataSourceBuilder
.create(dataSourceProperties.getClassLoader())
.driverClassName(dataSourceProperties.getDriverClassName())
.url(dataSourceProperties.getUrl())
.username(dataSourceProperties.getUsername())
.password(dataSourceProperties.getPassword())
.type(HikariDataSource.class)
.build();
dataSource.setMaximumPoolSize(maxPoolSize);
return dataSource;
}
/**
* Entity Manager Factory setup.
*
*/
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[] { "package-name" });
factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
factoryBean.setJpaProperties(jpaProperties());
return factoryBean;
}
/**
* Provider specific adapter.
*
*/
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
return hibernateJpaVendorAdapter;
}
/**
* Hibernate properties.
*
*/
private Properties jpaProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("datasource.myapp.hibernate.dialect"));
properties.put("hibernate.hbm2ddl.auto", environment.getRequiredProperty("datasource.myapp.hibernate.hbm2ddl.method"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("datasource.myapp.hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("datasource.myapp.hibernate.format_sql"));
if(StringUtils.isNotEmpty(environment.getRequiredProperty("datasource.myapp.defaultSchema"))){
properties.put("hibernate.default_schema", environment.getRequiredProperty("datasource.myapp.defaultSchema"));
}
return properties;
}
@Bean
@Autowired
public PlatformTransactionManager jpaTransactionManager(EntityManagerFactory emf) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(emf);
return txManager;
}
}
application.yaml
server:
port: 8081
servlet:
context-path: /CRUDApp
---
spring:
profiles: local,default
datasource:
myapp:
url: jdbc:h2:~/test
username: SA
password:
driverClassName: org.h2.Driver
defaultSchema:
maxPoolSize: 10
hibernate:
hbm2ddl.method: create-drop
show_sql: true
format_sql: true
dialect: org.hibernate.dialect.H2Dialect
---
Upvotes: 0