I want to change a property of a bean. I want to change it only once for performance (better when reading from XML), not in every bean instance instantiation. What is the best way to do it in Spring?
For elaborating and giving a concrete example:
Below is the datasource bean declaration in databaseContext.xml.
I want to decrypt ${jdbc.password} whose value is ENC(....) with JASYPT.
I could not do it with Jaspt Spring integration since Jaspt not compliant yet with Spring5 and not with Jasypt Hibernate integration since using a different datasource other than Hibernate.
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="poolName" value="springHikariCP" />
<property name="connectionTestQuery" value="SELECT 1 from dual" />
<property name="dataSourceClassName" value="oracle.jdbc.pool.OracleDataSource" />
<property name="maximumPoolSize" value="10" />
<property name="idleTimeout" value="30000" />
<property name="dataSourceProperties">
<props>
<prop key="url">${jdbc.url}</prop>
<prop key="user">${jdbc.user}</prop>
<prop key="password">${jdbc.password}</prop>
</props>
</property>
</bean>
This helped me a lot:
Spring property placeholder decrypt resolved property
Just want to add some small correction and extra notes about some findings:
In the link above it is written "location", but it must be "locations" as written below, where it resides at applicationContext.xml
<bean class="com.dummy.util.EncryptationAwarePropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:database.properties</value>
<value>classpath*:hibernate.properties</value>
</list>
</property>
</bean>
"PropertyPlaceholderConfigurer" is deprecated. But it still works. If you try to use the newly proposed class "PropertySourcesPlaceholderConfigurer", it has a bug that it does not call "convertPropertyValue" method as noted here: https://github.com/spring-projects/spring-framework/issues/13568. A workaround noted by the way there.
public class EncryptationAwarePropertySourcesPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
#Override
protected String convertPropertyValue(String originalValue) {
if (originalValue != null && originalValue.startsWith("ENC(")) {
return decrypt(originalValue);
}
return originalValue;
}
.
.
}
Related
I encouter an issue "Spring transaction fail rollback". I have a service class which call 2 DAO to insert data into DB table.
emTrcvLineDAO.create(lineVo) fail to insert into table as the lineVo missing some mandatory fields, but emTrcvHeaderDAO.create(vo) fail rollback and the data still inserted in to DB successfully. I am wondering why it does not rollback as the two DAO are in the same transaction.
Any guys have idea on this? Thanks in advance.
public void saveEmTrcvHeader(List<EmTrcvHeaderVOImpl> voList, List<ResponseItem> responseItemList) {
for (EmTrcvHeaderVOImpl vo : voList) {
emTrcvHeaderDAO.create(vo);
List<EmTrcvLineVOImpl> lineList = vo.getLineList();
for (int i = 0; i < lineList.size(); i++) {
EmTrcvLineVOImpl lineVo = lineList.get(i);
lineVo.setEmTrcvHeaderId(vo.getEmTrcvHeaderId());
lineVo.setProjId(null);
emTrcvLineDAO.create(lineVo);
}
}
}
My transaction configuration:
<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="proxyTargetClass">
<value>true</value>
</property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED,-java.lang.Exception,-org.springframework.dao.DataAccessException</prop>
<prop key="*">PROPAGATION_REQUIRED,-java.lang.Exception,-org.springframework.dao.DataAccessException</prop>
</props>
</property>
</bean>
My service and dao defined as below:
<bean name="emTrcvHeaderService" parent="txProxyTemplate">
<property name="target">
<bean class="com.emtrcv.service.EmTrcvHeaderService">
<property name="emTrcvHeaderDAO">
<ref bean="emTrcvHeaderDAO"/>
</property>
<property name="emTrcvPubSelectIdsDAO">
<ref bean="emTrcvPubSelectIdsDAO"/>
</property>
<property name="emTrcvLineDAO">
<ref bean="emTrcvLineDAO"/>
</property>
</bean>
</property>
</bean>
<bean name="emTrcvHeaderDAO" class="com.emtrcv.dao.EmTrcvHeaderDAOImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean name="emTrcvPubSelectIdsDAO" class="com.emtrcv.dao.EmTrcvPubSelectIdsDAOImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean name="emTrcvLineDAO" class="com.emtrcv.dao.EmTrcvLineDAOImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
I think you have to mention when rollback should take place.
As per doc,
The concept of rollback rules is important: they enable you to specify which exceptions (and throwables) should cause automatic rollback. You specify this declaratively, in configuration, not in Java code. So, although you can still call setRollbackOnly() on the TransactionStatus object to roll back the current transaction back, most often you can specify a rule that MyApplicationException must always result in rollback.
Please refer http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html for more details
Finally I found the root cause. there are two duplicate Spring configuration files both define transaction management rules in the project, it make spring tranasction management not work. After removed one of them, it works.
I am quite confused with Spring and Hibernate transactions. I have the following sample code.
I am wondering if
This is a correct way of retrieval or not.
Should I use getCurrentSession().beginTransaction() as well, should I use it in conjunction with #Transactional at all?
Configuration
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:2000/HiberProject" />
<property name="username" value="jack" />
<property name="password" value="jack" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
depends-on="dataSource">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.hiberproject.model" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
Service
#Service
public class SampleRecordsServiceImpl implements SampleRecordsService{
#Autowired
SampleRecordsRepository sampleRecordsRepository;
#Override
#Transactional(readOnly=true)
public Record retrieveRecord(long id){
return sampleRecordsRepository.retrieveRecord(id);
}
}
Repository
#Repository
public class SampleRecordsRepository implements SampleRecordsRepository{
#Autowired
SessionFactory sessioFactory;
#Override
public Record retrieveRecord(long id){
return (Record) sessionFactory.getCurrentSession().get(Record.class,id);
}
}
The #Transactional annotation itself defines the scope of a single database transaction. The database transaction happens inside the scope of a persistence context.
The persistence context is just a synchronizer object that tracks the state of a limited set of Java objects and makes sure that changes on those objects are eventually persisted back into the database.
For #Transactional annotation you can set propagation attribute, using Propagation you can handle your tarnsaction in different way like Propagation.REQUIRES_NEW(if you need new transaction on every request) . the default propagation is REQUIRED.
session.beginTransaction() will also either begin a new Transaction if one isn't present, or it will use an existing transaction to begin the unit of work specified.
So you should use either one of approach to manage the transaction.
Yes that's Okay to use only #Transactional annotation like this when you use Spring to manage your transaction.
No. You don't need to do that! If you use #Transactional annotation in your service, then Spring takes care of your persistence layer to manage transaction. All you need is to declare persistence layer specific transaction manager in your Spring configuration. So you do not need to manage transaction with hibernate sessions by using session.beginTransaction() together with #Transactional.
For more information please see the documentation of using #Transactional.
I have a question how to inject values into implemented by spring class I don't want use xml to define that values like in this piece
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="your.smtphost.com"/>
<property name="port" value="25"/>
<property name="username" value="yourusername"/>
<property name="password" value="yourpassword"/>
<property name="javaMailProperties">
<props>
<!-- Use SMTP-AUTH to authenticate to SMTP server -->
<prop key="mail.smtp.auth">true</prop>
<!-- Use TLS to encrypt communication with SMTP server -->
<prop key="mail.smtp.starttls.enable">true</prop>
</props>
</property>
I would like to do it by annotation. I know there is method to extend that class but maybe there is another?
You can use annotation driven spring configuration for this as shown below
#Configuration
class SpringConfiguration {
#Bean
public JavaMailSenderImpl mailSender(){
JavaMailSenderImpl object = new JavaMailSenderImpl();
object.setXXX(ABC);
.....
return object;
}
}
Scenario: In the application I have language-dependent property files which are used as templates to generate emails:
email-subscription_en.properties:
email.subject=You are successfully subscribed to list {0}
email.body=...
email-cancellation_en.properties:
email.subject=You are successfully unsubscribed from list {0}
email.body=...
and so on. Now in Spring context I would like to have these bundles:
<bean id="subscriptionMailProperties" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="org.company.email-subscription" />
</bean>
<bean id="cancellationMailProperties" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="org.company.email-cancellation" />
</bean>
be merged with these common language-independent properties which I would like to be declared in context:
<util:properties id="commonMailProperties">
<prop key="email.from">noreply#company.org</prop>
<prop key="email.to">{0}#company.org</prop>
</util:properties>
How is it possible?
As far as I know there is no support for this. You are trying to mix configuration with resource bundles. I feel what you currently have is right. If you do not have luxury of keeping it as it is, here is a way(more of a hack)
Implement org.springframework.context.MessageSource with 'commonMailProperties'(java.util.Properties) as dependency and say the bean id as 'commonMessageSource'.
In 'getMessage' implementations get the value from 'commonMailProperties'.
Inject 'commonMessageSource' to 'subscriptionMailProperties' and 'cancellationMailProperties', for 'parentMessageSource' property.
If somebody got interested in complete solution:
Create class PropertiesMessageSource:
/**
* {#link org.springframework.context.MessageSource} implementation that resolves messages via underlying
* {#link Properties}.
*/
public class PropertiesMessageSource extends AbstractMessageSource {
private Properties properties;
/**
* Set properties to use.
*/
public void setProperties(Properties properties) {
this.properties = properties;
}
#Override
protected MessageFormat resolveCode(String code, Locale locale) {
String property = properties.getProperty(code);
if (property == null) {
return null;
}
return createMessageFormat(property, locale);
}
}
Use it:
<bean id="commonMailProperties" class="org.company.PropertiesMessageSource">
<property name="properties">
<props>
<prop key="email.from">noreply#company.org</prop>
<prop key="email.to">{0}#company.org</prop>
</props>
</property>
</bean>
<bean id="subscriptionMailProperties" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="org.company.email-subscription" />
<property name="parentMessageSource">
<ref bean="commonMailProperties"/>
</property>
</bean>
ResourceBundleMessageSource (more exactly: all descendants of AbstractMessageSource) now has commonMessages property which can hold locale-independent values. For example while you want to have mail subject and body locale-dependant, some properties (mail from and mail to) are common across all bundles (check SPR-10291):
<bean id="mailProperties" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="org.mycompany.email" />
<property name="commonMessages">
<props>
<prop key="email.from">empty#mydomain.org</prop>
<prop key="email.to">%s#mydomain.org</prop>
</props>
</property>
</bean>
We use spring, velocity for email piece of code in our application and i got below exception. Any idea to solve this.
"org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'org.springframework.ui.velocity.VelocityEngineFactoryBean' to required type
'org.apache.velocity.app.VelocityEngine' for property 'velocityEngine'; nested exception is java.lang.IllegalStateException: Cannot convert value of type
[org.springframework.ui.velocity.VelocityEngineFactoryBean] to required type [org.apache.velocity.app.VelocityEngine] for property 'velocityEngine': no matching editors or conversion strategy found "
Following is my context configuration.
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="xxxx.US"/>
<property name="port" value="25"/>
<property name="javaMailProperties">
<props>
<prop key="mail.transport.protocol">smtp</prop>
<prop key="mail.smtp.auth">false</prop>
<prop key="mail.smtp.starttls.enable">false</prop>
<prop key="mail.debug">true</prop>
</props>
</property>
</bean>
<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
<property name="velocityProperties">
<value>
resource.loader=class
class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
</value>
</property>
</bean>
<bean id="emailUtil" class="com.example.MailUtil">
<property name="mailSender" ref="mailSender" />
<property name="velocityEngine" ref="velocityEngine" />
</bean>
You do not need to reference the velocityEngine in your emailUtil bean. Everything else about your spring config is correct.
I grabbed the following code from my app to show you how we are able to use velocity templates.
private String generateMessageFromTemplate(User user, VelocityTemplateEnum velocityTemplateEnum) {
VelocityContext context = new VelocityContext();
context.put("user", user);
StringWriter stringWriter = new StringWriter();
String template = velocityTemplateDao.findById(velocityTemplateEnum.name()).getTemplate();
Velocity.evaluate(context, stringWriter, velocityTemplateEnum.name(), template);
return stringWriter.getBuffer().toString();
}
I have a similar setup only I autowire in the VelocityEngine and that's working for me. Is autowiring an option for you? Also, can you add the code for your MailUtil class? Maybe something isn't 100% right with the velocityEngine field/setter?