Enabling exception translation for JPA in plain Spring - java

I was setting up a basic CRUD web app with JPA in plain Spring (no Spring Boot or Spring Data JPA) for educational purposes and faced a strange problem: Spring doesn't translate exceptions for my repository. According to the Spring documentation (here and here), it is sufficient to mark the repository with the #Repository annotation and Spring will automatically enable exception translation for this repository.
However, when I did so and triggered a UNIQUE constraint violation, I still was getting a JPA PersistenceException (with a Hibernate ConstraintViolationException inside) instead of the Spring DataIntegrityViolationException.
I used pure Java Spring configuration and it took me quite some time to realize that I should compare it with the XML configuration in the documentation. Compared to the pure Java configuration, the XML configuration adds a PersistenceExceptionTranslationPostProcessor into the context. When I added it manually with #Bean, it worked, but now I have a question.
Have I misconfigured something? The Spring documentation doesn't require registering that post-processor manually for pure Java configuration. Maybe there is another way to register it, say an #EnableXXX annotation?
Here is the summary of my configuration.
#Configuration
#ComponentScan("com.example.secured_crm")
public class SpringConfiguration {
// the problem is solved if I uncomment this
//#Bean
//public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
// return new PersistenceExceptionTranslationPostProcessor();
//}
}
#Configuration
#PropertySource("classpath:db.properties")
#EnableTransactionManagement
public class DataSourceConfiguration {
#Value("${jdbc.driver}")
private String driverClass;
#Value("${jdbc.url}")
private String url;
// ...
#Value("${hibernate.debug}")
private String hibernateDebug;
#Bean
public DataSource dataSource() {
var dataSource = new ComboPooledDataSource();
// ...
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
var emFactory = new LocalContainerEntityManagerFactoryBean();
emFactory.setDataSource(dataSource());
emFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
emFactory.setPackagesToScan("com.example.secured_crm.entities");
var properties = new Properties();
properties.setProperty("hibernate.dialect", hibernateDialect);
properties.setProperty("hibernate.show_sql", hibernateDebug);
properties.setProperty("hibernate.format_sql", "true");
emFactory.setJpaProperties(properties);
return emFactory;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
var txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}
public interface UserRepository {
User findByName(String username);
List<User> findAll();
void save(User user);
boolean deleteById(int id);
User findById(int id);
}
#Repository
public class UserJpaRepository implements UserRepository {
#PersistenceContext
EntityManager em;
#Override
public void save(User user) {
if (user.getId() == null) {
em.persist(user);
} else {
em.merge(user);
}
}
// and so on...
}
By the way, when I tried to add the post-processor in DataSourceConfiguration, it disabled #PropertySource effect. So far my impression of Spring is that it's one big hack...

It requires to manually register PersistenceExceptionTranslationPostProcessor in order for the exception translation to take effect.
The documentation you mentioned simply does not updated yet to show a fully working java configuration. It should mention to register this post processor. ( So feel free to provide a PR to update the docs.).
If you check from its javadoc , it already mentioned PersistenceExceptionTranslationPostProcessor is necessary to be registered :
As a consequence, all that is usually needed to enable automatic
exception translation is marking all affected beans (such as
Repositories or DAOs) with the #Repository annotation, along with
defining this post-processor as a bean in the application context.
P.S. If you are using spring-boot , and if it detects PersistenceExceptionTranslationPostProcessor is in the class-path , it will automatically register it by default such that you do not need to register manually.

Related

Spring 5 Unable to find a Repository

I have a Spring Framework 5.3.7 application, NOT Spring Boot (I'll move to Spring Boot later). I am using Java Configuration and it is working well so far. I have a multi-maven module project, and the first module is "myapp-entity". There is a config directory with the following file:
#Configuration
#PropertySource(value = "file:/opt/myapp/myapp-ws.properties")
#EnableTransactionManagement
public class AppEntityConfiguration
{
#Value("${hibernate.connection.driver.class}")
private String driverClassName;
#Value("${hibernate.connection.url}")
private String connectionUrl;
#Value("${hibernate.connection.username}")
private String username;
#Value("${hibernate.connection.password}")
private String password;
#Bean
public LocalSessionFactoryBean sessionFactory()
{
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan("com.app.model");
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource()
{
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(connectionUrl);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public PlatformTransactionManager hibernateTransactionManager()
{
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory().getObject());
return transactionManager;
}
private final Properties hibernateProperties()
{
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "none");
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
return hibernateProperties;
}
}
The myapp-entity.jar compiles fine with Java 11, and this is fine. The next maven module which is myapp-dao has a config directory and a configuration class.
#Configuration
#Import(AppEntityConfiguration.class)
#ComponentScan(basePackages = "com.app.dao")
public class RepositoryContextConfiguration {
}
I have an TableOneEntity defined with an #Entity annotation in the app-entity.jar and that is fine.
I have a TableOneDao and TableOneDaoImpl defined with a very basic list of functions.
public interface TableOneDao
{ ... list of functions }
#Repositories("tableOneDao")
public class TableOneDaoImpl implements TableOneDao
{ ... iplementation of functions }
And the test for this works perfectly:
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
#PropertySource(value = "file:/opt/myapp/myapp-ws.properties")
#ContextConfiguration(classes = RepositoryContextConfiguration.class)
public class OrganizationDaoTest extends BaseDaoTests
{
#Autowired
private TableOneDao tableOneDao;
}
This is very much the old way I used to do things, and it wall worked well. NOW, I want to get rid of this old way of doing things and go with a new way of doing things. Maybe I can't do them in the same project, that could be the issue.
I have a second Entity (TableTwoEntity) and a second Repository (TableTwoDao):
#Repository("clothesDryerDao")
public interface ClothesDryerDao extends JpaRepository<ClothesDryer, Long>
{
}
This is now a JPA repository and when I do a simple on this Dao, it cannot find THIS Dao.
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
#PropertySource(value = "file:/opt/ekotrope/ekotrope-ws.properties")
#ContextConfiguration(classes = RepositoryContextConfiguration.class)
public class ClothesDryerDaoTest extends TestCase
{
#Autowired
private ClothesDryerDao clothesDryerDao;
}
And I get the error as follows:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.ekotrope.dao.ClothesDryerDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1790)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1346)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657)
... 30 more
I thought I just need to modify the RepositoryContextConfiguration as follows:
#Configuration
#Import(EkotropeEntityConfiguration.class)
#ComponentScan(basePackages = "com.ekotrope.dao")
#EnableJpaRepositories(basePackages = "com.ekotrope.dao")
public class RepositoryContextConfiguration
{
}
But this did not work. As a matter of fact I think it broke the other working tests. So, the question is ... can I use both these methods (Dao and DaoImpl) and (JPA Dao)? Or, should I use only one, and if that was the case I would go with the JPA Repositories. I just wanted to be able to demonstrate both methods to my co-workers who are not familiar with Spring.
So, if I can get both to work at the same time, that would be great, but if not, then I can create one more Maven Module and then I will have a myapp-dao-old.jar and myapp-dao-new.jar.
Thanks for the help!
I think your jpaRepositories config is wrong but yout dao is good.
And I think this 2 solutions can work together.
Firtsly you don't need to put an #Repository on the ClothesDryerDao interface. (I understand you want to use a qualifier but i don't think this will work will not be necessary. (Type is enough for spring injection if you don't have multiple instance of a same class)
Secondly I think you need to change your Jpa configuration.
In a project i've done something like this
#Configuration
#EnableJpaRepositories(
basePackageClasses = ClothesDryerDao.class,
entityManagerFactoryRef = "configEntityManager",
)
public class RepositoryContextConfiguration{ /
//be careful this bean can maybe be in conflict with your sessionFactory
#Bean
public LocalContainerEntityManagerFactoryBean configEntityManager(DataSource dataSource) {
log.info("Start Parametrage Arcole Entity Manager");
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("com.app.model");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
}
Thirdly i think (this one is juste my opinion) you should maybe not mix classical Dao and Repository in the same package. For a better visibility :)
Even if the usage is the same

Is there a way to save an #Entity with pre-defined #EmbeddedId value using Spring Data's CrudRepository#save() method?

I'm creating a new service with the goal of consuming Kafka events in an idempotent manner and storing the data into a new PostgreSQL database.
The event will provide data which will be used in the composite key:
#Embeddable
public class MyCompositeKey implements Serializable {
#Column(name="field1", nullable = false)
private UUID field1;
#Column(name="field2", nullable = false)
private UUID field2;
#Column(name="field3", nullable = false)
private UUID field3;
... boilerplate Constructors/getters ...
And the Entity will be referencing it via #EmbeddedId:
#Entity
#Table
public class MyEntity implements Serializable {
#EmbeddedId private MyCompositeKey myCompositeKey;
... Columns/Constructors/getters ...
When an event is consumed, I want to let spring-data-jpa be smart enough to know whether we are replacing data from an existing MyEntity, or creating a new row.
The logic was deemed safe enough to use the CrudRepository#save method before researching the expectation of the logic within that method:
#Transactional
public <S extends T> S save(S entity) {
if (this.entityInformation.isNew(entity)) {
this.em.persist(entity);
return entity;
} else {
return this.em.merge(entity);
}
}
I've gotten to the point where the transactions appear to be completed, but no records are persisted to the table.
I've confirmed via debugging that the call to #save is branching into the return this.em.merge(entity) logic referenced above.
I've only found one possibly helpful blog post[1] for a similar scenario, and am lost on where to go next after it didn't seem to resolve the issue.
The only other option I can foresee is to manually go through a potential three-query execution:
findById
if exists, delete
save
Components
spring-boot-starter 2.0.6
spring-boot-starter-data-jpa 2.0.6
hibernate 5.2.x
References
[1] https://jivimberg.io/blog/2018/11/05/using-uuid-on-spring-data-jpa-entities/
Alright, I found the issue. All of this design was working fine, it was the configuration which was missing.
For some context - Spring Boot seems to configure default javax.sql.DataSource, default javax.persistence.EntityManagerFactory, and default org.springframework.transaction.PlatformTransactionManager beans.
My context was configured with a javax.sql.DataSource bean in order to specify a configuration prefix distinction using org.springframework.boot.context.properties.ConfigurationProperties.
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = {"com.myservice"})
public class RepositoryConfiguration {
#Bean
#ConfigurationProperties(prefix = "myservice.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
My context did not add in replacements for the dependent javax.persistence.EntityManagerFactory and org.springframework.transaction.PlatformTransactionManager beans.
The fix was to add in all of the configuration. From the docs:
You must create LocalContainerEntityManagerFactoryBean and not EntityManagerFactory directly, since the former also participates in exception translation mechanisms in addition to creating EntityManagerFactory.
The resulting configuration:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = {"com.myservice"})
public class RepositoryConfiguration {
#Bean
#ConfigurationProperties(prefix = "myservice.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.myservice");
factory.setDataSource(dataSource());
return factory;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
}

Issue with Declarative Transactions and TransactionAwareDataSourceProxy in combination with JOOQ

I have a data source configuration class that looks as follows, with separate DataSource beans for testing and non-testing environments using JOOQ. In my code, I do not use DSLContext.transaction(ctx -> {...} but rather mark the method as transactional, so that JOOQ defers to Spring's declarative transactions for transactionality. I am using Spring 4.3.7.RELEASE.
I have the following issue:
During testing (JUnit), #Transactional works as expected. A single method is transactional no matter how many times I use the DSLContext's store() method, and a RuntimeException triggers a rollback of the entire transaction.
During actual production runtime, #Transactional is completely ignored. A method is no longer transactional, and TransactionSynchronizationManager.getResourceMap() holds two separate values: one showing to my connection pool (which is not transactional), and one showing the TransactionAwareDataSourceProxy).
In this case, I would have expected only a single resource of type TransactionAwareDataSourceProxy which wraps my DB CP.
After much trial and error using the second set of configuration changes I made (noted below with "AFTER"), #Transactional works correctly as expected even during runtime, though TransactionSynchronizationManager.getResourceMap() holds the following value:
In this case, my DataSourceTransactionManager seems to not even know the TransactionAwareDataSourceProxy (most likely due to my passing it the simple DataSource, and not the proxy object), which seems to completely 'skip' the proxy anyway.
My question is: the initial configuration that I had seemed correct, but did not work. The proposed 'fix' works, but IMO should not work at all (since the transaction manager does not seem to be aware of the TransactionAwareDataSourceProxy).
What is going on here? Is there a cleaner way to fix this issue?
BEFORE (not transactional during runtime)
#Configuration
#EnableTransactionManagement
#RefreshScope
#Slf4j
public class DataSourceConfig {
#Bean
#Primary
public DSLContext dslContext(org.jooq.Configuration configuration) throws SQLException {
return new DefaultDSLContext(configuration);
}
#Bean
#Primary
public org.jooq.Configuration defaultConfiguration(DataSourceConnectionProvider dataSourceConnectionProvider) {
org.jooq.Configuration configuration = new DefaultConfiguration()
.derive(dataSourceConnectionProvider)
.derive(SQLDialect.POSTGRES_9_5);
configuration.set(new DeleteOrUpdateWithoutWhereListener());
return configuration;
}
#Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
#Bean
public DataSourceConnectionProvider dataSourceConnectionProvider(DataSource dataSource) {
return new DataSourceConnectionProvider(dataSource);
}
#Configuration
#ConditionalOnClass(EmbeddedPostgres.class)
static class EmbeddedDataSourceConfig {
#Value("${spring.jdbc.port}")
private int dbPort;
#Bean(destroyMethod = "close")
public EmbeddedPostgres embeddedPostgres() throws Exception {
EmbeddedPostgres embeddedPostgres = EmbeddedPostgresHelper.startDatabase(dbPort);
return embeddedPostgres;
}
#Bean
#Primary
public DataSource dataSource(EmbeddedPostgres embeddedPostgres) throws Exception {
DataSource dataSource = embeddedPostgres.getPostgresDatabase();
return new TransactionAwareDataSourceProxy(dataSource);
}
}
#Configuration
#ConditionalOnMissingClass("com.opentable.db.postgres.embedded.EmbeddedPostgres")
#RefreshScope
static class DefaultDataSourceConfig {
#Value("${spring.jdbc.url}")
private String url;
#Value("${spring.jdbc.username}")
private String username;
#Value("${spring.jdbc.password}")
private String password;
#Value("${spring.jdbc.driverClass}")
private String driverClass;
#Value("${spring.jdbc.MaximumPoolSize}")
private Integer maxPoolSize;
#Bean
#Primary
#RefreshScope
public DataSource dataSource() {
log.debug("Connecting to datasource: {}", url);
HikariConfig hikariConfig = buildPool();
DataSource dataSource = new HikariDataSource(hikariConfig);
return new TransactionAwareDataSourceProxy(dataSource);
}
private HikariConfig buildPool() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(username);
config.setPassword(password);
config.setDriverClassName(driverClass);
config.setConnectionTestQuery("SELECT 1");
config.setMaximumPoolSize(maxPoolSize);
return config;
}
}
AFTER (transactional during runtime, as expected, all non-listed beans identical to above)
#Configuration
#EnableTransactionManagement
#RefreshScope
#Slf4j
public class DataSourceConfig {
#Bean
public DataSourceConnectionProvider dataSourceConnectionProvider(TransactionAwareDataSourceProxy dataSourceProxy) {
return new DataSourceConnectionProvider(dataSourceProxy);
}
#Bean
public TransactionAwareDataSourceProxy transactionAwareDataSourceProxy(DataSource dataSource) {
return new TransactionAwareDataSourceProxy(dataSource);
}
#Configuration
#ConditionalOnMissingClass("com.opentable.db.postgres.embedded.EmbeddedPostgres")
#RefreshScope
static class DefaultDataSourceConfig {
#Value("${spring.jdbc.url}")
private String url;
#Value("${spring.jdbc.username}")
private String username;
#Value("${spring.jdbc.password}")
private String password;
#Value("${spring.jdbc.driverClass}")
private String driverClass;
#Value("${spring.jdbc.MaximumPoolSize}")
private Integer maxPoolSize;
#Bean
#Primary
#RefreshScope
public DataSource dataSource() {
log.debug("Connecting to datasource: {}", url);
HikariConfig hikariConfig = buildPoolConfig();
DataSource dataSource = new HikariDataSource(hikariConfig);
return dataSource; // not returning the proxy here
}
}
}
I'll turn my comments into an answer.
The transaction manager should NOT be aware of the proxy. From the documentation:
Note that the transaction manager, for example
DataSourceTransactionManager, still needs to work with the underlying
DataSource, not with this proxy.
The class TransactionAwareDataSourceProxy is a special purpose class that is not needed in most cases. Anything that is interfacing with your data source through the Spring framework infrastructure should NOT have the proxy in their chain of access. The proxy is intended for code that cannot interface with the Spring infrastructure. For example, a third party library that was already setup to work with JDBC and did not accept any of Spring's JDBC templates. This is stated in the same docs as above:
This proxy allows data access code to work with the plain JDBC API and
still participate in Spring-managed transactions, similar to JDBC code
in a J2EE/JTA environment. However, if possible, use Spring's
DataSourceUtils, JdbcTemplate or JDBC operation objects to get
transaction participation even without a proxy for the target
DataSource, avoiding the need to define such a proxy in the first
place.
If you do not have any code that needs to bypass the Spring framework then do not use the TransactionAwareDataSourceProxy at all. If you do have legacy code like this then you will need to do what you already configured in your second setup. You will need to create two beans, one which is the data source, and one which is the proxy. You should then give the data source to all of the Spring managed types and the proxy to the legacy types.

Using SpringBoot with Neo4J: Updating node creates duplicate node

I am trying to update a node on Neo4J, but what ends up happening is that it creates a duplicate Node. I read that the update has to be in a single transaction and I added #Transactional, but still same result. Here is what I have. I tried the approach of reading and deleting the old node, and saving the new one and it appears to be working. But, I think that is not the right approach. Why the #Transactional annotation not working. Thank you.
#EnableNeo4JRepositories(com.example.graph.repo)
#EnableTransactionManagement
#org.springframework.contect.annotation.Configuration
public class Neo4JConfig {
#Bean
public Configuration configuration() {
Configuration cfg = new Configuration();
cfg.driverConfiguration()
.setDriverClassName("org.neo4j.ogm.drivers.http.driver.HttpDriver")
.setURI("http://neo4j:neo4j#localhost:7474");
return cfg;
}
#Bean
public SessionFactory sessionFactory() {
return new SessionFactory(configuration(), "com.example");
}
#Bean
public Neo4jTransactionManager transactionManager() {
return new Neo4JTransactionManager(sessionFactory());
}
}
#Service
public class UserService{
#Autowired
UserRepository userRepository;
#Transactional
public void updateUser(User user) {
User existingUser = userRepository.getExistingUser(user.getUserName());
if(existingUser != null ) {
user.setSomeValue(existingUser.getSomeValue());
userRepository.save(user);
}
}
}
Spring AOP uses JDK Proxy mechanism by default. It means that you must invoke #Transactional method via interface method.
So you should split your service into interface UserService and implementation (say UserServiceImpl), autowire the interface into the code where you currently autowire the impementation, and then invoke transactional method via interface.
P.S. Another approach is to force Spring to use CGLIB as long as this mechanism is not limited to interfaces. More details for both mechanisms https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch08s06.html

Spring. How exception translation works?

I have following code:
#Repository
public class UserDAOImpl implements UserDAO {
public void addUser(User user) {
throw new HibernateException("unchecked exception");
}
}
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserDAO userDAO;
#Override
public void addUser(User user) {
try {
userDAO.addUser(user);
} catch (Exception e) {
e.printStackTrace();
}
}
}
web.xml:
<context:component-scan base-package="org.example.dao,
org.example.services"/>
<mvc:annotation-driven />
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
Is it enough to translate exception? Should I to implement custom translator?
It would be nice to get example code.
Basically, Spring Framework and other Spring related Projects (such as Spring Data NoSQL), define a thorough hierarchy of persistence exception inheriting from org.springframework
.dao.DataAccessException. This hierarchy takes care of all the exception that are thrown by JDBC, Hibernate, other ORM implemntations, and JPA, etc.
In your webapplication, You need to do two things to achieve exception translation.
First, configure org.springframework.dao.support
.PersistenceExceptionTranslator implementation(s) in your configuration files. You can do this by defining a bean of LocalContainerEntityManagerFactoryBean in your root config class like this:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
Map<String, Object> properties = new Hashtable<>();
properties.put(
"javax.persistence.schema-generation.database.action",
"none");
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
//you will chose a dialect that you are using for your project.
adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect");
LocalContainerEntityManagerFactoryBean factory =
new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(adapter);
factory.setDataSource(this.springJpaDataSource());
//the packages that contains you #Repositories annotations
factory.setPackagesToScan("packagenames");
factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
factory.setValidationMode(ValidationMode.NONE);
factory.setJpaPropertyMap(properties);
return factory;
}
This class LocalContainerEntityManagerFactoryBean implements PersistenceExceptionTranslator. So you have taken care of first step.
The second step is to mark your repositories/entities with #Repository annotation. This tells Spring that the annotated bean is eligible for exception translation using the
configured PersistenceExceptionTranslators. If the repository methods throw any persistence
exceptions, the PersistenceExceptionTranslators translate those exceptions as appropriate.
Excerpts from Professional Java for Web Applications by Nicholas S. Williams.

Categories