I am a bit confused about your example... What are you trying to do? Create a user? What does it have to do with emails? Do you mean an "email address", as the user identifier? Or are you talking about some confirmation email being sent out once the user is successfully created? If the answer is the former, than EmailExistsException is a very confusing misnomer, in the first place. If the latter is the case, you probably should issue the confirmation email in a separate transaction that would not affect the creation of the user.
Assuming that your "email" means "the user ID"... Here's what I would do, roughly:
Service:
Code:
public class UserServiceImpl implements UserService {
private UserDao dao; // reference to the data access object used by the service
/**
* Sets the reference to the DAO implementation for this service.
* @param theDao dao instance
*/
@Required
public void setDao(UserDao theDao) {
dao = theDao;
}
/**
* Creates a new user from the given User object and returns the updated object for the new user.
* @param theDao dao instance
* @return updated user object (perhaps, with the new unique ID generated, or/and additional status info, etc.)
*/
@Transactional
public User createUser(User user) {
// any business logic (if needed)
return dao.insertNewUser(user); // or, perform add'l logic and then return
}
... other methods here
}
The DAO:
Code:
public class UserDaoImpl extends [whatever Spring DAO support you are using] implements UserDao {
...
@Transactional
public User insertNewUser(User user) {
User existingUser = findExistingUser(user.getEmailAddress());
if (existingUser == null) {
// perform an insert, perhaps get new user ID, and return updated user object
} else {
throw new DuplicateUserException("A user with email address " + existingUser.getEmailAddress + " already exists; Existing User database ID = " + existingUser.getid()); // this message will only be displayed for debugging in lof file, not in the UI. You will map the exception to the appropriate UI view/message
}
}
...
// this method may also be public if you find that useful
private User findExistingUser(String userEmailAddress) {
// lookup user by email address
}
}
This is what I would prefer. Nice and simple. No try/catches, just a very
legitimate check for an
important business condition and one throw. (Needless to say, DuplicateUserException is a RTE because no one needs to be aware of it except your designated resolver on the presentation tier side.)
Note that simply wrapping the whole DAO method in the try/catch assuming that ANY exception (or any constraint violation) would mean the fact that the user already exists - and not some other problem - is unreliable. So, you treat you very legitimate and expected business case very consciously and specifically - by checking exactly what you need to check. The rest would just mean a critical failure in the DA tier, and won't carry any additional meaning for your application. So you would just want it to freely propagate to the top handler/resolver, get logged with the full stack trace, and result in a lovely generic error page that does not scare the pants off the user.
Also, keep in mind that if you nest transactional methods (e.g. the dao method is nested within the service method), by default, any exception within the wrapping method will roll back any successful transaction within the nested method - unless you specify otherwise in the Transactional annotation properties.
HTH,
C