Below are a few Subversion reminders on how to branch, merge and rollback changes.
To create a branch from the HEAD of the trunk:
svn copy http://<server>/<svn repo>/<svn project>/branches/<branch name>
You can add a -m <comment> onto the operation to provide a reason for the branch.
To delete a branch:
svn delete http://<server>/<svn repo>/<svn project>/branches/<branch name>
If for some reason you want to include changes from the trunk post-branch, then you can use the merge command from the branch root directory. A useful precursor to this is to use the dry run argument as below:
svn merge --dry-run -r2602:HEAD http://<server>/<svn repo>/<svn project>/trunk
NOTE: 2602 is non-inclusive i.e. revisions merged will start from 2603 onwards.
Now do the actual merge:
svn merge -r2602:HEAD http://<server>/<svn repo>/<svn project>/trunk
No doubt there will be conflicts so once resolved in your favourite IDE, check the changes in.
The above commands assume you noted down the revision number (2602) when branching. If not known, then from the branch root directory, execute the below command:
svn log -v --stop-on-copy
To merge branch changes into the trunk, the merge command can be used in similar fashion from the trunk root directory:
svn merge -r2602:HEAD http://<server>/<svn repo>/<svn project>/branches/<branch name>
If you want to rollback commited changes, then you can use the merge command with the -r argument:
svn merge -r10:9 http://<server>/<svn repo>/<svn project>/trunk
To rollback a commit that took place a while ago and others that have been committed since, use the merge command again:
svn merge -r7:5 http://<server>/<svn repo>/<svn project>/trunk
NOTE: Note that this will revert revisions 7 and 6. Revision 5 is the target revision that we want to keep.
Many thanks to Rob Legg of Asset Source Ltd for the above summary.
Random mutterings, but mainly reminders for myself, on all things Java,
Wednesday, 16 February 2011
Subversion Reminders - Branching, Merging and Rollbacks
Monday, 14 February 2011
Unit testing managed beans using Mockito
Mockito is a powerful testing framework which is ideal for unit testing managed beans. It allows developers to mock existing classes thereby enabling the behaviour of those classes to be manipulated by the developer depending upon what the aim of the test is.
A simple managed bean which is to be tested is below:
import java.io.Serializable;
import java.math.BigDecimal;
import javax.annotation.Named;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
@Named("customerManager")
@ApplicationScoped
public class CustomerManager implements Serializable {
private static final long serialVersionUID = 1L;
private AccountManager accountManager;
@Inject
public void setAccountManager(
final AccountManager accountManager) {
this.accountManager = accountManager;
}
public boolean isInCredit() {
boolean inCredit = false;
BigDecimal currentAccountBalance =
this.accountManager.getBalance(AccountType.CURRENT);
BigDecimal depositAccountBalance =
this.accountManager.getBalance(AccountType.DEPOSIT);
BigDecimal accountBalance =
new BigDecimal(currentAccountBalance.doubleValue())
.add(depositAccountBalance);
if (accountBalance.compareTo(BigDecimal.ZERO) > 0) {
inCredit = true;
}
return inCredit;
}
}
The test class for CustomerManager could include various scenarios depending on what is returned by the AccountManager class. An example test class is below:
import java.math.BigDecimal;
import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.times;
import static junit.framework.Assert.assertTrue;
public class CustomerManagerTest {
private CustomerManager customerManager;
private AccountManager mockAccountManager =
mock(AccountManager.class);
@Before
public void setUp() throws Exception {
customerManager = new CustomerManager();
customerManager.setAccountManager(mockAccountManager);
}
@Test
public void testPositiveBalance() {
BigDecimal positiveAmount = new BigDecimal(1000.00);
when(mockAccountManager.getBalance(((AccountType)any())))
.thenReturn(positiveAmount);
boolean inCredit = customerManager.isInCredit();
assertTrue(inCredit);
verify(mockAccountManager, times(2))
.getBalance((AccountType)any());
}
}
The when is mocking calls to the AccountManager getBalance method for any given parameter of type AccountType. As the method should have been called twice (once for CURRENT and once for DEPOSIT), the verify call can determine the number of getBalance method invocations. A more fine grained test equivalent of the above could be:
@Test
public void testPositiveBalance() {
BigDecimal positiveAmount = new BigDecimal(1000.00);
when(mockAccountManager.getBalance(AccountType.CURRENT))
.thenReturn(positiveAmount);
when(mockAccountManager.getBalance(AccountType.DEPOSIT))
.thenReturn(positiveAmount);
boolean inCredit = customerManager.isInCredit();
assertTrue(inCredit);
verify(mockAccountManager)
.getBalance(AccountType.CURRENT);
verify(mockAccountManager)
.getBalance(AccountType.DEPOSIT);
}
The argument matchers are more explicit in the above and could also return different results if needs be.
It is also possible to mock consecutive calls to a method so the same argument can generate a different return object. Using the getBalance example, the first call could return 1000.0 and the next 2000.0.
BigDecimal positiveAmount1 = new BigDecimal(1000.00);
BigDecimal positiveAmount2 = new BigDecimal(2000.00);
when(mockAccountManager.getBalance(((AccountType)any())))
.thenReturn(positiveAmount1)
.thenReturn(positiveAmount2);
It is also possible to partially mock real objects using the spy method. If a real AccountManager object had been created and set, then the getBalance method could have be stubbed as shown below:
import java.math.BigDecimal;
import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static junit.framework.Assert.assertTrue;
public class CustomerManagerTest {
private CustomerManager customerManager;
private AccountManager accountManager = new AccountManager();
@Before
public void setUp() throws Exception {
customerManager = new CustomerManager();
}
@Test
public void testPositiveBalance() {
AccountManager spyAccountManager = spy(accountManager);
customerManager.setAccountManager(spyAccountManager);
BigDecimal postiveAmount = new BigDecimal(1000.00);
when(spyAccountManager.getBalance(((AccountType)any())))
.thenReturn(postiveAmount);
boolean inCredit = customerManager.isInCredit();
assertTrue(inCredit);
verify(spyAccountManager, times(2))
.getBalance((AccountType)any());
}
}
There are many more features to the Mockito framework than described here (throwing exceptions, stubbing void methods, etc..) and http://mockito.org/ is a good starting point.
Finally, you can also use annotations to declare mock objects. An alternative to using
private AccountManager mockAccountManager =
mock(AccountManager.class);
would be to use the @Mock annotation as below:
@Mock
private AccountManager mockAccountManager;
and in the setup method initialise them by the following command:
MockitoAnnotations.initMocks(this);
A simple managed bean which is to be tested is below:
import java.io.Serializable;
import java.math.BigDecimal;
import javax.annotation.Named;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
@Named("customerManager")
@ApplicationScoped
public class CustomerManager implements Serializable {
private static final long serialVersionUID = 1L;
private AccountManager accountManager;
@Inject
public void setAccountManager(
final AccountManager accountManager) {
this.accountManager = accountManager;
}
public boolean isInCredit() {
boolean inCredit = false;
BigDecimal currentAccountBalance =
this.accountManager.getBalance(AccountType.CURRENT);
BigDecimal depositAccountBalance =
this.accountManager.getBalance(AccountType.DEPOSIT);
BigDecimal accountBalance =
new BigDecimal(currentAccountBalance.doubleValue())
.add(depositAccountBalance);
if (accountBalance.compareTo(BigDecimal.ZERO) > 0) {
inCredit = true;
}
return inCredit;
}
}
The test class for CustomerManager could include various scenarios depending on what is returned by the AccountManager class. An example test class is below:
import java.math.BigDecimal;
import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.times;
import static junit.framework.Assert.assertTrue;
public class CustomerManagerTest {
private CustomerManager customerManager;
private AccountManager mockAccountManager =
mock(AccountManager.class);
@Before
public void setUp() throws Exception {
customerManager = new CustomerManager();
customerManager.setAccountManager(mockAccountManager);
}
@Test
public void testPositiveBalance() {
BigDecimal positiveAmount = new BigDecimal(1000.00);
when(mockAccountManager.getBalance(((AccountType)any())))
.thenReturn(positiveAmount);
boolean inCredit = customerManager.isInCredit();
assertTrue(inCredit);
verify(mockAccountManager, times(2))
.getBalance((AccountType)any());
}
}
The when is mocking calls to the AccountManager getBalance method for any given parameter of type AccountType. As the method should have been called twice (once for CURRENT and once for DEPOSIT), the verify call can determine the number of getBalance method invocations. A more fine grained test equivalent of the above could be:
@Test
public void testPositiveBalance() {
BigDecimal positiveAmount = new BigDecimal(1000.00);
when(mockAccountManager.getBalance(AccountType.CURRENT))
.thenReturn(positiveAmount);
when(mockAccountManager.getBalance(AccountType.DEPOSIT))
.thenReturn(positiveAmount);
boolean inCredit = customerManager.isInCredit();
assertTrue(inCredit);
verify(mockAccountManager)
.getBalance(AccountType.CURRENT);
verify(mockAccountManager)
.getBalance(AccountType.DEPOSIT);
}
The argument matchers are more explicit in the above and could also return different results if needs be.
It is also possible to mock consecutive calls to a method so the same argument can generate a different return object. Using the getBalance example, the first call could return 1000.0 and the next 2000.0.
BigDecimal positiveAmount1 = new BigDecimal(1000.00);
BigDecimal positiveAmount2 = new BigDecimal(2000.00);
when(mockAccountManager.getBalance(((AccountType)any())))
.thenReturn(positiveAmount1)
.thenReturn(positiveAmount2);
It is also possible to partially mock real objects using the spy method. If a real AccountManager object had been created and set, then the getBalance method could have be stubbed as shown below:
import java.math.BigDecimal;
import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static junit.framework.Assert.assertTrue;
public class CustomerManagerTest {
private CustomerManager customerManager;
private AccountManager accountManager = new AccountManager();
@Before
public void setUp() throws Exception {
customerManager = new CustomerManager();
}
@Test
public void testPositiveBalance() {
AccountManager spyAccountManager = spy(accountManager);
customerManager.setAccountManager(spyAccountManager);
BigDecimal postiveAmount = new BigDecimal(1000.00);
when(spyAccountManager.getBalance(((AccountType)any())))
.thenReturn(postiveAmount);
boolean inCredit = customerManager.isInCredit();
assertTrue(inCredit);
verify(spyAccountManager, times(2))
.getBalance((AccountType)any());
}
}
There are many more features to the Mockito framework than described here (throwing exceptions, stubbing void methods, etc..) and http://mockito.org/ is a good starting point.
Finally, you can also use annotations to declare mock objects. An alternative to using
private AccountManager mockAccountManager =
mock(AccountManager.class);
would be to use the @Mock annotation as below:
@Mock
private AccountManager mockAccountManager;
and in the setup method initialise them by the following command:
MockitoAnnotations.initMocks(this);
Tuesday, 8 February 2011
JPA Entity Lifecycle Callback and Listener Annotations
Within the javax.persistence package there are annotations that may be applied to methods of an entity or a mapped superclass to specify that the annotated method must be called for a certain lifecycle event. A lifecycle event being either: Persist, Update, Remove or Load. and each event has a Pre or Post event.
Listeners can also have methods annotated with a lifecycle event. Listeners can be used to hold the business logic that would otherwise be in the entities (or another layer.) This enables the entities to be just POJOs. It also means that the testing of the business logic can be done in isolation.
Below is an example of using lifecycle events to populate an id var and also populate a lastUpdated var, in a mapped superclass:
@MappedSuperclass
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
private static final int ID_LENGTH=36;
@Id
@Column(length = ID_LENGTH)
private String id;
@Temporal(TemporalType.TIMESTAMP)
@Column(updatable = false)
private Date dateCreated;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModified;
// constructor(s)
@PreUpdate
public void updateLastModified () {
lastModified = new Date();
}
@PrePersist
protected void generateUUID() {
id = java.util.UUID.randomUUID().toString();
dateCreated = new Date();
lastModified = new Date();
}
.....
}
An simple example of using a Listener could be where an entity has a transient var that needs to be populated after the entity has been persisted, updated or loaded.
public class AvailableCreditListener {
@PostLoad
@PostPersist
@PostUpdate
public void calculateAvailableCredit{Account account) {
account.setAvailableCredit(
account.getBalance().add(
account.getOverdraftLimit()));
}
}
The entity class would be annotated with @EntityListeners:
@EntityListeners({AvailableCreditListener.class})
public class AccountEntity extends BaseEntity {
private BigDecimal balance;
private BigDecimal overdraftLimit;
@Transient
private BigDecimal availableCredit;
// getters and setters
}
Finally, instead of annotations, an XMl mapping file can be used and deployed with the application to specify default listeners. (This mapping file is referenced by the persistence.xml file.) But an entity can use the @ExcludeDefaultListeners annotation if it does not want to use the default listeners.
Listeners can also have methods annotated with a lifecycle event. Listeners can be used to hold the business logic that would otherwise be in the entities (or another layer.) This enables the entities to be just POJOs. It also means that the testing of the business logic can be done in isolation.
Below is an example of using lifecycle events to populate an id var and also populate a lastUpdated var, in a mapped superclass:
@MappedSuperclass
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
private static final int ID_LENGTH=36;
@Id
@Column(length = ID_LENGTH)
private String id;
@Temporal(TemporalType.TIMESTAMP)
@Column(updatable = false)
private Date dateCreated;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModified;
// constructor(s)
@PreUpdate
public void updateLastModified () {
lastModified = new Date();
}
@PrePersist
protected void generateUUID() {
id = java.util.UUID.randomUUID().toString();
dateCreated = new Date();
lastModified = new Date();
}
.....
}
An simple example of using a Listener could be where an entity has a transient var that needs to be populated after the entity has been persisted, updated or loaded.
public class AvailableCreditListener {
@PostLoad
@PostPersist
@PostUpdate
public void calculateAvailableCredit{Account account) {
account.setAvailableCredit(
account.getBalance().add(
account.getOverdraftLimit()));
}
}
The entity class would be annotated with @EntityListeners:
@EntityListeners({AvailableCreditListener.class})
public class AccountEntity extends BaseEntity {
private BigDecimal balance;
private BigDecimal overdraftLimit;
@Transient
private BigDecimal availableCredit;
// getters and setters
}
Finally, instead of annotations, an XMl mapping file can be used and deployed with the application to specify default listeners. (This mapping file is referenced by the persistence.xml file.) But an entity can use the @ExcludeDefaultListeners annotation if it does not want to use the default listeners.
@ExcludeDefaultListeners
@Entity
public class AccountEntity extends BaseEntity {
....
}
Tuesday, 1 February 2011
Loose Coupling using CDI
A key to n-tier architectures is notion of loose coupling and CDI enables this between classes in Java EE 6.
By using the @Inject annotation, you can specify an injection point in a class. The below example uses the annotation on a setter method.
@Inject
public void setCustomer(Customer customer) {
this.customer = customer;
}
It could equally be used on a field as below:
@Inject
Customer customer;
It is possible that more than one implementation of the Customer class exists so to resolve this problem at runtime, qualifiers are used. They provide the ability to 'qualify' what gets injected. an example qualifier is below:
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Qualifier
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface TradeCustomerQualifier {
}
The TradeCustomer class would be enhanced to use the qualifier by way of an annotation.
@TradeCustomerQualifier
@Named("tradeCustomer")
@ApplicationScoped
public class TradeCustomer extends Customer {
......
}
An example of using the qualifier is below:
@Inject
public void setCustomer(@TradeCustomerQualifier Customer customer) {
this.customer = customer;
}
Finally, in order for CDI to bootstrap, there'll need to be a beans.xml file in the META-INF folder (for jars and in the WEB-INF for web applications.)
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
By using the @Inject annotation, you can specify an injection point in a class. The below example uses the annotation on a setter method.
@Inject
public void setCustomer(Customer customer) {
this.customer = customer;
}
It could equally be used on a field as below:
@Inject
Customer customer;
It is possible that more than one implementation of the Customer class exists so to resolve this problem at runtime, qualifiers are used. They provide the ability to 'qualify' what gets injected. an example qualifier is below:
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Qualifier
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface TradeCustomerQualifier {
}
The TradeCustomer class would be enhanced to use the qualifier by way of an annotation.
@TradeCustomerQualifier
@Named("tradeCustomer")
@ApplicationScoped
public class TradeCustomer extends Customer {
......
}
An example of using the qualifier is below:
@Inject
public void setCustomer(@TradeCustomerQualifier Customer customer) {
this.customer = customer;
}
Finally, in order for CDI to bootstrap, there'll need to be a beans.xml file in the META-INF folder (for jars and in the WEB-INF for web applications.)
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
Thursday, 27 January 2011
Extending Bean Functionality using Interceptors
A key part of the Java EE 6 platform is CDI (Context and Dependency Injection) and CDI supports two ways of extending bean functionality: interceptors and decorators.
For this post, we'll concentrate on interceptors and provide an example of implementing a cross-cutting concern across multiple beans ie logging.
An example of an interceptor which extracts the user value from the SessionContext for logging is shown below:
import org.apache.log4j.MDC;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import java.io.Serializable;
public final class AuditLoggingInterceptor implements Serializable {
protected static final String MDC_USER = "user";
protected static final String UNAUTHENTICATED_USER =
"Unauthenticated User";
protected static final String NULL_USER =
"Authenticated User Name is Null";
private static final long serialVersionUID = 1L;
private SessionContext sessionContext;
@AroundInvoke
public Object captureUser(InvocationContext context)
throws Exception {
MDC.put(MDC_USER, determineUsername());
try {
return context.proceed();
}catch(Exception e){
// handle exception
}finally {
MDC.remove(MDC_USER);
}
}
/**
* Inject the calling Session Context.
* @param sessionContext context the current invocation
*/
@Resource
public void setSessionContext(SessionContext sessionContext) {
this.sessionContext = sessionContext;
}
public SessionContext getSessionContext() {
return sessionContext;
}
/**
* Determine what the username should be based on the session context.
*
* @return the username
*/
protected String determineUsername() {
String username = NULL_USER;
if (sessionContext != null) {
try{
if (sessionContext.getCallerPrincipal() == null) {
username = UNAUTHENTICATED_USER;
} else {
username =
sessionContext.getCallerPrincipal().getName();
}
}catch(NullPointerException npe){
// handle exception
}
}
return username;
}
}
The above interceptor implements a captureUser method which is marked with the @AroundInvoke annotation. This ensures that the method is invoked around the business methods for the classes that the interceptor is bound to.
import org.apache.log4j.Logger;
import javax.interceptor.Interceptors;
import javax.ejb.Remote;
import javax.ejb.Stateless;
@Interceptors({AuditLoggingInterceptor.class})
@Stateless(mappedName = JndiResourceName.BUSINESS_SERVICE)
@Remote(BusinessService.class)
public class BusinessServiceBean implements BusinessService {
private static final Logger LOGGER =
Logger.getLogger(BusinessServiceBean .class);
@Override
public void doSomething() {
LOGGER.info("BusinessServiceBean.doSomething ()");
// do something
}
}
When the doSomething method is invoked on the business service, the method will be wrapped by captureUser method. The log4j class MDC (Mapped Diagnostic Context) will set the user key in the context map to be the caller principal name from the session context. This value will be used when the info message is logged. When the method completes, the user value is removed from the context.
The entry in the log4j.properties file so that the MDC value gets picked up is below:
log4j.appender.AUDITROLLINGFILE.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [%X{user}] %c{1} [%p] %m%n
To exclude a class's methods from being 'intercepted' then the business methods can be annotated with @ExcludeClassInterceptors.
For this post, we'll concentrate on interceptors and provide an example of implementing a cross-cutting concern across multiple beans ie logging.
An example of an interceptor which extracts the user value from the SessionContext for logging is shown below:
import org.apache.log4j.MDC;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import java.io.Serializable;
public final class AuditLoggingInterceptor implements Serializable {
protected static final String MDC_USER = "user";
protected static final String UNAUTHENTICATED_USER =
"Unauthenticated User";
protected static final String NULL_USER =
"Authenticated User Name is Null";
private static final long serialVersionUID = 1L;
private SessionContext sessionContext;
@AroundInvoke
public Object captureUser(InvocationContext context)
throws Exception {
MDC.put(MDC_USER, determineUsername());
try {
return context.proceed();
}catch(Exception e){
// handle exception
}finally {
MDC.remove(MDC_USER);
}
}
/**
* Inject the calling Session Context.
* @param sessionContext context the current invocation
*/
@Resource
public void setSessionContext(SessionContext sessionContext) {
this.sessionContext = sessionContext;
}
public SessionContext getSessionContext() {
return sessionContext;
}
/**
* Determine what the username should be based on the session context.
*
* @return the username
*/
protected String determineUsername() {
String username = NULL_USER;
if (sessionContext != null) {
try{
if (sessionContext.getCallerPrincipal() == null) {
username = UNAUTHENTICATED_USER;
} else {
username =
sessionContext.getCallerPrincipal().getName();
}
}catch(NullPointerException npe){
// handle exception
}
}
return username;
}
}
The above interceptor implements a captureUser method which is marked with the @AroundInvoke annotation. This ensures that the method is invoked around the business methods for the classes that the interceptor is bound to.
import org.apache.log4j.Logger;
import javax.interceptor.Interceptors;
import javax.ejb.Remote;
import javax.ejb.Stateless;
@Interceptors({AuditLoggingInterceptor.class})
@Stateless(mappedName = JndiResourceName.BUSINESS_SERVICE)
@Remote(BusinessService.class)
public class BusinessServiceBean implements BusinessService {
private static final Logger LOGGER =
Logger.getLogger(BusinessServiceBean .class);
@Override
public void doSomething() {
LOGGER.info("BusinessServiceBean.doSomething ()");
// do something
}
}
When the doSomething method is invoked on the business service, the method will be wrapped by captureUser method. The log4j class MDC (Mapped Diagnostic Context) will set the user key in the context map to be the caller principal name from the session context. This value will be used when the info message is logged. When the method completes, the user value is removed from the context.
The entry in the log4j.properties file so that the MDC value gets picked up is below:
log4j.appender.AUDITROLLINGFILE.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [%X{user}] %c{1} [%p] %m%n
To exclude a class's methods from being 'intercepted' then the business methods can be annotated with @ExcludeClassInterceptors.
Saturday, 22 January 2011
Second Level Caching
JPA has two levels of caching. The first level of caching is the persistence context (which can be either transaction scoped or extended.), and the second level of caching, introduced in JPA 2.0, sites in between the entity manager and the database.
With second level caching entities not found in the persistence context will be loaded from the second level cache, and if not found there, from the database. The ideal type of entity to live in the second level cache are those that are rarely updated, or those that are constantly read.
To mark an entity as requiring caching, you can use the @Cacheable annotation as below:
@Entity
@Cacheable(true)
public class Person {
@Id @GeneratedValue
private Long id;
private String name;
// etc ...
}
To override the provider-specific defaults for managing cached entities, you can set the shared-cache-mode value in the persistence.xml. The possible values are below:
Finally, you cannot implement a caching strategy without also considering a locking strategy (but that's for another post!)
With second level caching entities not found in the persistence context will be loaded from the second level cache, and if not found there, from the database. The ideal type of entity to live in the second level cache are those that are rarely updated, or those that are constantly read.
To mark an entity as requiring caching, you can use the @Cacheable annotation as below:
@Entity
@Cacheable(true)
public class Person {
@Id @GeneratedValue
private Long id;
private String name;
// etc ...
}
To override the provider-specific defaults for managing cached entities, you can set the shared-cache-mode value in the persistence.xml. The possible values are below:
- ALL- all entities are cached
- DISABLE_SELECTIVE - all entities cached apart from those with the annotation @Cacheable(false)
- ENABLE_SELECTIVE - only entities with the annotation @Cacheable(true) are cached
- NONE - no caching for the persistence unit
- UNSPECIFIED - provider-specific default
Finally, you cannot implement a caching strategy without also considering a locking strategy (but that's for another post!)
Friday, 21 January 2011
CriteriaBuilder and Dynamic Queries in JPA 2.0
A major new feature of Java EE 6 is JPA 2.0 and in particular the addition of the Criteria API which provides the ability to dynamically construct object-based queries.
This resolves some of the problems which arise when building dynamic native queries. The below example shows how to find customer entities with two search parameters:
The problem with the above is that it is not type safe and involves iterating over a List of Object where those Objects are themselves Object arrays. Also should Customer contain any child elements, these would have to be retrieved in a separate call.
Using the CriteriaBuilder, the same results can be achieved as shown below:
There is some type safety in the above but it can be furthered tied down by using the metamodel class for the entity, by using the metamodel class's public static members instead of text strings for the entity's attributes. The code would now look like this:
Having built metamodel classes using Maven, it's questionable whether it's a worthwhile exercise as any mistakes in the text based approach to finding attribute names should be flagged up by comprehensive unit testing.
This resolves some of the problems which arise when building dynamic native queries. The below example shows how to find customer entities with two search parameters:
public List<CustomerEntity> findCustomers(
final String firstName, final String surname) {
StringBuilder queryBuilder = new StringBuilder(
"select c from Customer where ");
List<String> paramList = new ArrayList<String>();
paramList.add(" upper(c.firstName) like '%?%'"
.replace("?", firstName.toUpperCase()));
paramList.add(" upper(c.surname) like '%?%'"
.replace("?", surname.toUpperCase()));
Iterator itr = paramList.iterator();
while(itr.hasNext()) {
queryBuilder.append(itr.next());
if (itr.hasNext()) {
queryBuilder.append(" and ");
}
}
final Query query = entityManager.createNativeQuery(
queryBuilder.toString());
List<Object> resultList = (List<Object>)query.getResultList();
// iterate, cast, populate and return a list
}
The problem with the above is that it is not type safe and involves iterating over a List of Object where those Objects are themselves Object arrays. Also should Customer contain any child elements, these would have to be retrieved in a separate call.
Using the CriteriaBuilder, the same results can be achieved as shown below:
public List<CustomerEntity> findCustomers(final String firstName, final String surname) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<CustomerEntity> query = builder.createQuery(CustomerEntity.class);
Root<CustomerEntity> cust = query.from(CustomerEntity.class);
query.select(cust);
List<Predicate> predicateList = new ArrayList<Predicate>();
Predicate firstNamePredicate, surnamePredicate;
if ((firstName != null) && (!(firstName.isEmpty()))) {
firstNamePredicate = builder.like(
builder.upper(cust.<String>get("firstName")), "%"+firstName.toUpperCase()+"%");
predicateList.add(firstNamePredicate);
}
if ((surname != null) && (!(surname.isEmpty()))) {
surnamePredicate = builder.like(
builder.upper(cust.<String>get("surname")), "%"+surname.toUpperCase()+"%");
predicateList.add(surnamePredicate);
}
Predicate[] predicates = new Predicate[predicateList.size()];
PredicateList.toArray(predicates);
query.where(predicates);
return entityManager.createQuery(query).getResultList();
}
There is some type safety in the above but it can be furthered tied down by using the metamodel class for the entity, by using the metamodel class's public static members instead of text strings for the entity's attributes. The code would now look like this:
firstNamePredicate = builder.like(
builder.upper(cust.get(CustomerEntity_.firstName)),
"%"+firstName.toUpperCase()+"%");
surnamePredicate = builder.like(
builder.upper(cust.get(CustomerEntity_.surname)),
"%"+surname.toUpperCase()+"%");
Having built metamodel classes using Maven, it's questionable whether it's a worthwhile exercise as any mistakes in the text based approach to finding attribute names should be flagged up by comprehensive unit testing.
Subscribe to:
Posts (Atom)