springboot auto config TransactionManager and SqlFactory, it works well ,but when i use #Transactional the program wait in a daze, i flow the code and find
beanFactory.getBean(PlatformTransactionManager.class) is very slow.
but when i add this:
#Autowired
private PlatformTransactionManager platformTransactionManager;
it works. so what's the problem. i have no idea, and i'm using grpc with springboot now, please help thx;
#Configuration
#SpringBootApplication
#ComponentScan(value = "cn.com.autohome.autopay.wallet")
#EnableTransactionManagement
#EnableAutoConfiguration
#EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
public class AppMain extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(AppMain.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(AppMain.class);
}
}
#Configuration
#MapperScan(basePackages = "cn.com.autohome.autopay.wallet.core.dal.mapper")
#ImportResource(locations = {"classpath:wallet-dal-bean.xml"})
public class WalletMyBatisConfig {
#Autowired
private DataBaseProperties dataBaseProperties;
#Primary
#Bean(name = "walletDataSource")
#ConfigurationProperties("spring.datasource.druid")
public DataSource masterDataSource() throws SQLException {
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
dataSource.setDriverClassName(dataBaseProperties.getWallet_data_source_driver_class());
dataSource.setUrl(dataBaseProperties.getWallet_data_source_url());
dataSource.setUsername(dataBaseProperties.getWallet_data_source_username());
dataSource.setPassword(dataBaseProperties.getWallet_data_source_password());
return dataSource;
}
}
Related
I am trying to understand a spring project which uses MongoDB drivers to connect to a db.
The application.properties file has the following properties defined:
spring.data.mongodb.database=${db.name:demo}
spring.data.mongodb.auto-index-creation=false
spring.data.mongodb.uri=mongodb://some-connection-string
And I see two relevant pieces of code in the project:
Application.java
#SpringBootApplication
#EnableCaching
#EnableScheduling
#ComponentScan({ "com.paans.*" })
#PropertySource("classpath:routes.properties")
public class Application extends SpringBootServletInitializer {
public static MongoTemplate _mongoTemplate;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory, MongoMappingContext context) {
MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory), context);
converter.setTypeMapper(new DefaultMongoTypeMapper(null));
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, converter);
mongoTemplate.setReadPreference(ReadPreference.nearest());
_mongoTemplate = mongoTemplate;
return mongoTemplate;
}
}
MongoConfiguration.java
#Configuration
public class MongoConfiguration {
#Autowired
MongoTemplate mongoTemplate;
#Autowired
MongoConverter mongoConverter;
#EventListener(ApplicationReadyEvent.class)
public void initIndicesAfterStartup() {
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext = this.mongoConverter.getMappingContext();
if (mappingContext instanceof MongoMappingContext) {
MongoMappingContext mongoMappingContext = (MongoMappingContext) mappingContext;
for (BasicMongoPersistentEntity<?> persistentEntity : mongoMappingContext.getPersistentEntities()) {
Class<?> clazz = persistentEntity.getType();
if (clazz.isAnnotationPresent(Document.class)) {
IndexResolver resolver = new MongoPersistentEntityIndexResolver(mongoMappingContext);
IndexOperations indexOps = mongoTemplate.indexOps(clazz);
resolver.resolveIndexFor(clazz).forEach(indexOps::ensureIndex);
}
}}}}
Now, I am trying to understand the execution sequence of this code and which piece of code is actually picking up the properties set up in application.properties. Any help ?
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
}
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 am trying to build a new annotation based spring boot application.
On the DAO-level I have a configuration class with Dao beans:
#Configuration
#EnableTransactionManagement
public class DatabaseConfig {
#Bean
public DataSource dataSource(){
...
return dataSource;
}
#Bean
public JdbcTemplate jdbcTemplate(){
JdbcTemplate template = new JdbcTemplate();
template.setDataSource(dataSource());
return template;
}
#Bean
public DataSourceInitializer dataSourceInitializer(DataSource dataSource)
{
....
}
#Bean
public PersonDao personDao(){
return new PersonDao();
}
}
On the server level a controller and a Mein class.
#SpringBootApplication (exclude = SecurityAutoConfiguration.class)
#ComponentScan("my.package")
#Import(DataBaseConfig.class)
public class Main {
private static Logger log = Logger.getLogger(Main.class);
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
The problem is that the main class does not see the beans got from the DatabaseConfig and that's why cannot start the application (because they are user in the controller). How can I correctly import them?