I have achieved using AspectJ to add #Transactional to non-Bean class and non-public methods. However, I still can't make self-invocation success.
This is my transcation manager config class
#Configuration
#EnableTransactionManagement
public class DBConfig {
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager() {
DataSourceTransactionManager txManager = new DataSourceTransactionManager(DATA_SOURCE);
AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager);
return txManager;
}
}
This is my loadtime weaver config
#Configuration
#EnableLoadTimeWeaving
public class AspectJConfig implements LoadTimeWeavingConfigurer {
#Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new InstrumentationLoadTimeWeaver();
}
}
And this is my self-invocation codes
public class Test {
#Transactional
public void testA() {
testB();
//......
}
#Transactional(propagation = Propagation.NEVER)
public void testB() {
//......
}
}
When I call testA, It's expected that it will throw exception because I have defined the propagation as NEVER. However, actually nothing happened.
So would anyone help me???
Related
i would like to load #Configuration classes in an order. i have two configuration classes. i am having a requirement of loading my SampleProperties class before sampleconfiguration class.
I have tried the following annotations but it is not working as expected.
#AutoConfigureAfter(SampleProperties.class )
#AutoConfigureBefore(SampleConfiguration.class)
I have put my congiurations class in diff package in order to read configurations classes in an order.using #Import function, i am including my configuration classes into my application
My Main Class:
#Import({SampleProperties.class,SampleConfiguration.class,})
public class SampleApplication{
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
}
My SampleProperties Class
#Configuration
#AutoConfigureBefore(SampleConfiguration.class)
#ConfigurationProperties("demo")
#Data
public class SampleProperties {
private String team;
private int teamSize;
private String teamLeader;
}
My sampleconfiguration Class:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef="sampleEntityManager",
transactionManagerRef="sampleTransactionManager",
basePackages= {"com.example.demo.repo"})
#AutoConfigureAfter(SampleProperties.class)
public class SampleConfiguration {
#Autowired
Environment env;
#Bean(name="sampleDataSource")
#Primary
public DataSource dmsDataSource() {
// functions
return null;
}
#Primary
#Bean(name = "sampleEntityManager")
public LocalContainerEntityManagerFactoryBean dmsEntityManagerFactory(EntityManagerFactoryBuilder builder) {
// functions
return null;
}
#Primary
#Bean(name = "sampleTransactionManager")
public PlatformTransactionManager dmsTransactionManager(#Qualifier("sampleEntityManager") EntityManagerFactory entityManagerFactory) {
// functions
return null;
}
}
can anyone tell me what missing and where am making mistakes?
I think you have to use #Order annotation.
#Component
#Order(1)
public class SampleProperties {
// code
}
#Component
#Order(2)
public class SampleConfiguration {
// code
}
when i am using #autowire to inject my dependencies in Configuration
class its giving me as null please refer the code below .
#Configuration
public class DataSourceConfig {
#Autowired
AppService appService;
#Bean
public BeanDefinitionRegistryPostProcessor beanPostProcessor() {
return new BeanDefinitionRegistryPostProcessor() {
public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException {
}
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanRegistry) throws BeansException {
createBeans(beanRegistry);
}
};
}
private void createBeans(BeanDefinitionRegistry beanRegistry,DataSourceConfigService ds) {
appService.getDbDetails();
appService is null here if i will call it using this way
BeanDefinitionRegistryPostProcessor beanPostProcessor(AppService
appService) then in AppServiceImpl class AppDao dependency will be null
}
}
//// Service
#Service
public class AppServiceImpl implements AppService{
#Autowired
AppDao ds;
#Override
public List<A> getDatabaseConfiguration() {
return ds.getDbDetails(); // here ds is null
}
}
//dao
#Repository
public class AppDaoImpl implements AppDao {
#Qualifier("nameParamJdbcTemplate")
#Autowired
public NamedParameterJdbcTemplate nameParamJdbcTemplate;
#Override
public List<A> getDbDetails() {
return nameParamJdbcTemplate.query(SELECT_QUERY, new DataSourceMapper()); // nameParamJdbcTemplate is null
}
// datasource config
#Configuration
public class DataSourceBuilderConfig {
#Bean(name = "dbSource")
#ConfigurationProperties(prefix = "datasource")
#Primary
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
#Bean(name = "nameParamJdbcTemplate")
#DependsOn("dbSource")
#Autowired
public NamedParameterJdbcTemplate jdbcTemplate1(#Qualifier("dbSource") DataSource dbSource) {
return new NamedParameterJdbcTemplate(dbSource);
}
}
What i want is when ever my beanPostProcessor()
is executed i want all my dependent beans should be instantiated ie
#Autowired
AppService appService;
#Autowired
AppDao ds;
#Qualifier("nameParamJdbcTemplate")
#Autowired
public NamedParameterJdbcTemplate nameParamJdbcTemplate;
I am new to spring so any help or working examples would be great. Thanks
It is null because this #Configuration class also defines a BeanDefinitionRegistryPostProcessor that forces the context to create that bean very early on.
Because you are using field injection, the context has to resolve AppService bean but it can't yet because the post-processor have to be applied first.
Your configuration looks very complex so you may want to simplify it a bit:
Separate low-level infrastructure configuration from main configuration
Always define such post processor as public static method so that the context can invoke the #Bean method without having to construct the class first.
I use #EntityListeners to make operations before I save in my Db and after I load.
Inside my Listener class I make a call to an Ecryptor (which needs to fetch info from configuration file), so the encryptor can't be called statically and need to be injected in my Listener. Right?
Well, injections in EntityListeners can't be done straight away, but you have some methods to do that, like using SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); or even the method showed here. https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/
Cool, the problem is: None of the solutions support unit testing! When running tests that encryptor I had injected in my model Listener is always null.
Here SpringBeanAutowiringSupport does not inject beans in jUnit tests There is a solution to create this context and pass to a instantiated object, but it does not solve my problem since I have the "Injection" to add to it.
Any way to create a context in my tests and somehow pass it to my listeners?
If not, any way I can create a static method to my Encryptor and still have access to the Environment API to read my properties?
Package Listener:
public class PackageListener{
#Autowired
Encryptor encryptor;
#PrePersist
public void preSave(final Package pack){
pack.setBic(encryptor.encrypt(pack.getBic()));
}
...
My test
#Test
#WithuserElectronics
public void testIfCanGetPackageById() throws PackageNotFoundException{
Package pack = packagesServiceFactory.getPackageService().getPackage(4000000002L);
}
Package service
public Package getPackage(Long id) throws PackageNotFoundException{
Package pack = packageDao.find(id);
if (pack == null) {
throw new PackageNotFoundException(id);
}
return pack;
}
Encryptor:
public class Encryptor{
private String salt;
public Encryptor(String salt){
this.salt = salt;
}
public String encrypt(String string){
String key = this.md5(salt);
String iv = this.md5(this.md5(salt));
if (string != null) {
return encryptWithAesCBC(string, key, iv);
}
return string;
}
...
You can create a DemoApplicationContextInitializer class to store the appliationContext reference in a static property in your main class.
public class DemoApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext ac) {
Application.context = ac;
}
}
#SpringBootApplication
public class Application {
public static ApplicationContext context;
public static void main(String[] args) throws Exception {
new SpringApplicationBuilder(Application.class)
.initializers(new DemoApplicationContextInitializer())
.run(args);
}
}
Then you can access the context in your entity listener
public class PackageListener{
//#Autowired
Encryptor encryptor;
#PrePersist
public void preSave(final Package pack){
encryptor = Application.context.getBean(Encryptor.class);
pack.setBic(encryptor.encrypt(pack.getBic()));
}
}
And to make this work in your junit test, just add the initializer in your test like this ...
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT, classes = Application.class)
#ContextConfiguration(classes = Application.class, initializers = DemoApplicationContextInitializer.class)
public class MyTest {
...
}
It works without any issue in my environment. Hope it will be helpful to you too.
To answer what you need, you have to create 2 classes that will do all the configuration needed.
You have to create a testConfig with the next annotations:
#Configuration
#ComponentScan(basePackages = { "yourPath.services.*",
"yourPath.dao.*" })
#EnableAspectJAutoProxy
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "yourPath.dao.entities",
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager",
repositoryBaseClass = Dao.class)
#Import({ DataSourceConfig.class }) //Explained below
public class TestConfig {
#Autowired
private DataSource dataSource;
#Bean
public List<String> modelJPA() {
return Collections.singletonList("es.carm.sms.ortopedia.entities");
}
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setPackagesToScan(modelJPA().toArray(new String[modelJPA().size()]));
entityManagerFactory.setDataSource(this.dataSource);
JpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter);
return entityManagerFactory;
}
}
Then if you want to connect with your database:
#Configuration
public class DataSourceConfig {
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.OracleDriver");
dataSource.setUrl("jdbc:oracle:thin:#ip:port:sid");
dataSource.setUsername("name");
dataSource.setPassword("pass");
return dataSource;
}
}
Now you have it all set up, you just need to create your test importing your configurations:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class)
public class TestCase {...}
You will get your spring context initialized with access to all your resources (MVC) Services, DAO and Model.
I have a spring boot application and use camel with it, I read a file and then I try to inserted on my DB, everything is working good the only problem is that I try to use #transactional or transactionTemplate to make a rollback when an error occur but it doesn't make the rollback,
With the #transactional I add to my SpringBootApplication the #EnableTransactionManagement(proxyTargetClass=true) and in my class I add the #Transactional(rollbackFor = Exception.class)
These are my classes:
#SpringBootApplication
#EnableDiscoveryClient
#EnableTransactionManagement(proxyTargetClass=true)
public class MsArchivo510Application {
public static void main(String[] args) {
SpringApplication.run(MsArchivo510Application.class, args);
}
}
#Service
public class ArchivoBS implements Processor{
#Transactional(rollbackFor = Exception.class)
#Override
public void process(Exchange exchange) throws Exception {
//Here I execute stored procedure and one of them fail
}
}
With the transactioTemplate my class end up like this:
#Service
public class ArchivoBS implements Processor{
#Override
public void process(Exchange exchange) throws Exception {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
//Here I execute stored procedure and one of them fail
} catch (Exception e) {
e.printStackTrace();
status.setRollbackOnly();
}
}
});
}
}
Am I missing something?, Can someone help me with this issue?,
Thanks in advance
You're in a camel context and Spring-boot may have difficulties to work properly.
You could try to make your transaction operation in a spring service and inject it in you processor then add #Transaction on your service method and call it from your processor.
At the end I noticed that I need to specify to my data source the DataSourceTransactionManager, I have a class with the annotation #Configuration and there I can create multiples data source, so my class were like this:
#Configuration
public class Configuracion {
#Bean(name = "mysqlNocturno")
#ConfigurationProperties(prefix = "spring.nocturno")
public DataSource mysqlDataSourceNocturno() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcTemplateNocturno")
public JdbcTemplate jdbcTemplateNocturno(#Qualifier("mysqlNocturno") DataSource dsMySQL) {
return new JdbcTemplate(dsMySQL);
}
#Bean(name = "mysqlProduccion")
#Primary
#ConfigurationProperties(prefix = "spring.produccion")
public DataSource mysqlDataSourceProduccion() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcTemplateProduccion")
public JdbcTemplate jdbcTemplateProduccion(#Qualifier("mysqlProduccion") DataSource dsMySQL) {
return new JdbcTemplate(dsMySQL);
}
}
The documentation mention that the annotation #EnableTransactionManagement need to be added on my SpringBootApplication class but that is not necessary, it need to be added on my configuration class, so my class end up like this:
#Configuration
#EnableTransactionManagement
public class Configuracion {
#Bean(name = "mysqlNocturno")
#ConfigurationProperties(prefix = "spring.nocturno")
public DataSource mysqlDataSourceNocturno() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcTemplateNocturno")
public JdbcTemplate jdbcTemplateNocturno(#Qualifier("mysqlNocturno") DataSource dsMySQL) {
return new JdbcTemplate(dsMySQL);
}
#Bean(name = "transactionManagerNocturno")
public PlatformTransactionManager transactionManagerNocturno() {
return new DataSourceTransactionManager(mysqlDataSourceNocturno());
}
#Bean(name = "mysqlProduccion")
#Primary
#ConfigurationProperties(prefix = "spring.produccion")
public DataSource mysqlDataSourceProduccion() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcTemplateProduccion")
public JdbcTemplate jdbcTemplateProduccion(#Qualifier("mysqlProduccion") DataSource dsMySQL) {
return new JdbcTemplate(dsMySQL);
}
#Bean(name = "transactionManagerProduccion")
public PlatformTransactionManager transactionManagerProduccion() {
return new DataSourceTransactionManager(mysqlDataSourceProduccion());
}
}
With this configuration I only need to add the #transactional annotation to my class like #Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
#Override
public void altaArchivo(Mensaje objMensaje, ArchivoCarnet objArchivoCarnet, ArchivoCarnetTrailer objArchivoCarnetTrailer, List<ArchivoCarnetDetalle> lstArchivoCarnetDetalle) {
if (objMensaje.getStrCodigo().equals(ArchivoErrorEnum.OPERACION_EXITOSA.getStrCodigo())) {
archivoDAO.altaArchivoCarnet(objArchivoCarnet);
archivoDAO.altaArchivoCarnetTrailer(objArchivoCarnetTrailer);
archivoDAO.altaArchivoCarnetDetalle(lstArchivoCarnetDetalle);
} else {
archivoDAO.altaBitacoraArchivo510(new BitacoraArchivo510(objMensaje, objArchivoCarnet.getStrNombre()));
}
}
Hope this help someone else :)
I'm trying to configure Spring Transactions but they seem not to work.
This is my config Java Class where I give definitions to Datasource, JdbcTemplates and all the Daos that I use
#Configuration
#PropertySource("${configuration.properties.path}")
#EnableTransactionManagement
public class BeanConfiguration {
...different DAOs...
#Override
#Bean
public DataSourceTransactionManager transactionManager(){
return new DataSourceTransactionManager(dataSource());
}
#Override
#Bean
public MyManager myManager(){
return new MyManager();
}
}
This is the class MyManager that has Transactible methods and operates with multiple DAOs.
//#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
#Transactional(propagation = Propagation.REQUIRED, rollbackFor=Exception.class)
public class EnoceanDeviceManager{
....autowired Daos
public void addNewDevices(Class1 class1, Class2 class2) {
//returns with id
class1 = 1dao.push(class1);
class2.setClass1(class1);
class2 = 2dao.push(class2); //can throw exception
}
}
I want that DB changes made with the help of 1dao do not occure if the method dao.push throws an Exception. There is no try-catches within DAO-level.
This is the class where I use one exemplar of MyManager to push a pair of objects together:
public class TestEnoceanDeviceManager extends BigClassForTestsWITHCOnfigurations{
#Atowired
MyManager myManager;
#Test
//the method
public void testAddNewDevices(){
.... defining class1 and class2....
myManager.addNewDevices(class1, class2);
}
}
As a result the method testAddNewDevices only throws a non-catched Exception from 2dao.push() method and doesn't log about rolling back. I tried to catch an Exception on different levels, but there is still no rolling back.
What am I doing wrong?