How to make Spring Data JPA repository return an exception on no result? - spring-data-jpa

I'm migrating an existing project to Spring Data JPA repositories.
In the old project my DAOs looks like this:
public class MovieDAOImp implements MovieDAO {
public Movie findByTitle(String title) throws NoResultException {
/... query the db, if no Movie, throw new NoResultException */
}
}
The new Spring JPA implementation looks like this ...
public interface MovieRepository extends JpaRepository<Movie, Integer> {
Movie findByTitle(String title);
}
In the new version findByTitle will return null if there's no Movie, but I want it to throw an Exception (preferably my NoResultException, but any exception will do).
The problem I have is all of my Services are implemented expecting the NoResultException to occur.
I don't want to re-work all of my Services to check for nulls.
Is there a way to get the JpaRepository to throw an exception when there is no result?

I was unable to find a direct answer to my question, so I came up with this workaround ...
I continue to implement my own DAO and use the repo internally.
This has the advantage of giving me complete control over DAO interface, including the exceptions that get thrown, so I don't have to re-work my services.
But it has the disadvantage of adding an extra layer.
public class MovieDAOImp implements MovieDAO {
#Autowired
private MovieRepository repo;
public Movie findByTitle(String title) throws NoResultException {
Movie ret = repo.findByTitle(title);
if (ret == null) {
throw new NoResultException("Unable to find movie by title '"+title+"'.");
}
return ret;
}
}
If anyone comes up with a better way to do this, please post an answer!

Related

How to remove/handle irrelevant or bad sort parameters from http url using Pageable interface in spring boot?

How to remove/handle irrelevant or bad sort parameters from http url using Pageable interface in spring boot?
For e.g. I have a query like
http://localhost:8080/all?sort=firstName,asc&sort=nosuchfield,asc
How can I handle or remove the irrelevant field "nosuchfield"?
Also, how can I limit sort parameters in URL?
If the sorting field doesn't present in the database then below exception will be thrown by Spring JPA.
org.springframework.data.mapping.PropertyReferenceException: No property nosuchfield found for type <TYPE>!
at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:94)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:382)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:358)
However, the exception can be handled using various types. Ultimately, you can just log it or transform it into any custom exception. As per my requirement, I have transformed it into a custom exception.
Using AOP
#Aspect
#Component
public class UnKnownColumnSortingExceptionHandler {
#AfterThrowing(pointcut = "execution(* com.repositorypackage.*.*(..))", throwing = "exception")
public void executeWhenExceptionThrowninRepository(JoinPoint jp, Throwable ex) {
if (ex instanceof PropertyReferenceException) {
throw new CustomException("Invalid Database operation");
}
}
}
Using #ControllerAdvice(Exception handling in Application wise)
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
public GlobalExceptionHandler() {}
#ExceptionHandler({PropertyReferenceException.class})
public ResponseEntity<Void> handleAllExceptions(Exception ex, WebRequest req) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Exception handling in Controller wise
Add the below piece of code to your controller
#ExceptionHandler({PropertyReferenceException.class})
public ResponseEntity<Void> handleAllExceptions(Exception ex, WebRequest req)
{
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}

Standalone JTA 1.2 and Hibernate: JPA not rolling back?

I'm using the "JBoss quickstart" tutorial described here.
It demonstrates the use of distributed transactions including JPA in standalone applications.
I've downloaded the code, runs fine, all test cases are green.
It contains the following test case:
#Test
public void testJpa() throws Exception {
System.out.println(testEntityRepository.save(new TestEntity("test1")));
System.out.println(testEntityRepository.save(new TestEntity("test2")));
System.out.println(testEntityRepository.save(new TestEntity("test3")));
org.junit.Assert.assertEquals(3, testEntityRepository.findAll().size());
}
I wanted to make this more interesting, by starting a transaction and rolling it back before the assert, like this:
#Test
public void testJpa() throws Exception {
transactionManager.begin();
System.out.println(testEntityRepository.save(new TestEntity("test1")));
System.out.println(testEntityRepository.save(new TestEntity("test2")));
System.out.println(testEntityRepository.save(new TestEntity("test3")));
transactionManager.rollback();
org.junit.Assert.assertEquals(0, testEntityRepository.findAll().size());
}
With the rollback() I would expect the findAll().size() to return 0. However it continues to return 3. Is there something I'm missing? The ability to roll back the JPA state would seem to be one of the main goals of the tutorial?
The original code for TestEntityRepository:
public class TestEntityRepository {
#Inject
EntityManager entityManager;
#Transactional
public List<TestEntity> findAll() {
assert entityManager != null;
return (List<TestEntity>) this.entityManager.createQuery("select te from TestEntity te").getResultList();
}
#Transactional
public Long save(TestEntity testEntity) {
assert entityManager != null;
if (testEntity.isTransient()) {
entityManager.persist(testEntity);
entityManager.flush();
} else {
entityManager.merge(testEntity);
entityManager.flush();
}
return testEntity.getId();
}
}
Other code can be found here.
Looks like a bug in the quickstart. I've raised an issue to fix it: https://issues.jboss.org/browse/JBTM-2668.

#Inject not working in AttributeConverter

I have a simple AttributeConverter implementation in which I try to inject an object which have to provide the conversion logic, but #Inject seem not to work for this case. The converter class looks like this:
#Converter(autoApply=false)
public class String2ByteArrayConverter implements AttributeConverter<String, byte[]>
{
#Inject
private Crypto crypto;
#Override
public byte[] convertToDatabaseColumn(String usrReadable)
{
return crypto.pg_encrypt(usrReadable);
}
#Override
public String convertToEntityAttribute(byte[] dbType)
{
return crypto.pg_decrypt(dbType);
}
}
When the #Converter is triggered it throws an NullPointerException because the property crypto is not being initialized from the container. Why is that?
I'm using Glassfish 4 and in all other cases #Inject works just fine.
Is it not possible to use CDI on converters?
Any help will be appreciated :)
The accent of my question is more the AttributeConverter part. I understand that for the CDI to work a bean must meet the conditions described here http://docs.oracle.com/javaee/6/tutorial/doc/gjfzi.html.
I also have tried to force the CDI to work by implementing the following constructor:
#Inject
public String2ByteArrayConverter(Crypto crypto)
{
this.crypto = crypto;
}
And now I got the following exception which doesn't give me any clue:
2015-07-23T01:03:24.835+0200|Severe: Exception during life cycle processing
org.glassfish.deployment.common.DeploymentException: Exception [EclipseLink-28019] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Deployment of PersistenceUnit [PU_VMA] failed. Close all factories for this PersistenceUnit.
Internal Exception: Exception [EclipseLink-7172] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Error encountered when instantiating the class [class model.converter.String2ByteArrayConverter].
Internal Exception: java.lang.InstantiationException: model.converter.String2ByteArrayConverter
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.createDeployFailedPersistenceException(EntityManagerSetupImpl.java:820)
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:760)
...
I even tried using #Producer or #Decorator in order to have the CDI working on that place, but I still think there is something specific with the AttributeConverter which doesn't allow CDI. So problem not solved yet.
Unfortunately you can't inject CDI beans into a JPA converter, however in CDI 1.1 you can inject your Crypto programmatically :
Crypto crypto = javax.enterprise.inject.spi.CDI.current().select(Crypto.class).get()
For reference, JPA 2.2 will allow CDI to be used with AttributeConverter, and some vendors already support this (EclipseLink, DataNucleus JPA are the ones I know of that do it).
You're trying to combine two different worlds, as CDI doesn't know about JPA Stuff and vice-versa. (One annotation parser of course doesn't know about the other)
What you CAN do, is this:
/**
* #author Jakob Galbavy <code>jg#chex.at</code>
*/
#Converter
#Singleton
#Startup
public class UserConverter implements AttributeConverter<User, Long> {
#Inject
private UserRepository userRepository;
private static UserRepository staticUserRepository;
#PostConstruct
public void init() {
staticUserRepository = this.userRepository;
}
#Override
public Long convertToDatabaseColumn(User attribute) {
if (null == attribute) {
return null;
}
return attribute.getId();
}
#Override
public User convertToEntityAttribute(Long dbData) {
if (null == dbData) {
return null;
}
return staticUserRepository.findById(dbData);
}
}
This way, you would create a Singleton EJB, that is created on boot of the container, setting the static class attribute in the PostConstruct phase. You then just use the static Repository instead of the injected field (which will still be NULL, when used as a JPA Converter).
Well, CDI still doesn't work for AttributeConverter, which would be the most elegant solution, but I have found a satisfying workaround. The workaround is using #FacesConverter. Unfortunately per default CDI doesn't work in faces converters and validators either, but thanks to the Apache MyFaces CODI API you can make it work unsing the #Advaced annotation :) So I came up with an implementation like this:
#Advanced
#FacesConverter("cryptoConverter")
public class CryptoJSFConverter implements Converter
{
private CryptoController crypto = new CryptoController();
#Inject
PatientController ptCtrl;
public Object getAsObject(FacesContext fc, UIComponent uic, String value)
{
if(value != null)
return crypto.pg_encrypt(value, ptCtrl.getSecretKey());
else
return null;
}
public String getAsString(FacesContext fc, UIComponent uic, Object object)
{
String res = crypto.pg_decrypt((byte[]) object, ptCtrl.getSecretKey());
return res;
}
}
The injected managed bean has to be explicitly annotated with #Named and some scope definition. A declaration in faces-config.xml doesn't work! In my solution it looks like this:
#Named
#SessionScoped
public class PatientController extends PersistanceManager
{
...
}
Now one has a context information in the converter. In my case it is session/user specific cryptography configuration.
Of course in such a solution it is very likely that a custom #FacesValidator is also needed, but thanks to CODI one have the possibility for using CDI here also (analog to converter).

Mongo custom repository autowired is null

I try to autowire my custom mongo repository (and it seems the constructor is executed) but still the result is null
I've looked at some similar questions
Spring Data Neo4j - #Autowired Repository == null
and
spring data mongo repository is null
but I still don't know how to solve this.
public class TestRepo {
#Autowired
PersonRepository repository;
public void find(String name)
{
System.out.println(repository.findByName(name));
}
}
config
<mongo:repositories base-package="com.yyyy.zzz" />
PersonRepository
public interface PersonRepository extends Repository<Person, BigInteger> {
#Query("{name : ?0}")
public Person findByName(String name);
}
Implementation
public class PersonRepositoryImpl implements PersonRepository{
PersonRepositoryImpl()
{
System.out.println("constructing");
}
public Person findByName(String name) {
...
}
}
if I get the repository bean directly from context it works
Your repository setup looks suspicious. To execute query methods, you don't need to provide an implementation at all. I suspect in your current setup the custom implementation you have in PersonRepositoryImpl "overrides" the query method and thus will be preferred on execution.
If you simply drop your implementation class, Spring Data will automatically execute the query for you on invocation.
Generally speaking, custom implementation classes are only needed for functionality you cannot get through other means (query methods, Querydsl intergration etc.).

Ejb 3 lookup not working in Jboss AS 6

I have migrated my app from Glassfish 2.2.1 to JBoss AS 6.
Previously i have a generic class for lookup the ejbs, Below is the code:
public class BeanUtil {
private static InitialContext ctx;
public static <T> T create(Class<T> type){
try {
ctx = new InitialContext();
return (T)ctx.lookup(type.getName());
} catch (NamingException e) {
e.printStackTrace();
}
return null;
}
}
The above class works fine for lookup in glassfish.
But after migrating the code to JBoss AS 6. I always get javax.naming.NameNotFoundException
Below is one of my ejb class.
#Remote
public interface OperationPerformed {
public void addRandomNo(String randomNos);
}
#Stateless
#Remote(OperationPerformed.class)
public class OperationPerformedImpl implements OperationPerformed {
public void addRandomNo(String randomNos) {
}
}
If i give the complete jndi name which jboss made during deployment of my app, then i can easily lookup the same. But i want generic code for lookup.
Below is the code which i invoke for lookup a bean:
OperationPerformed operationPerformed =
BeanUtil.create(OperationPerformed.class);
Please suggest if i am doing something wrong.
I think you can convert class name to jndi name and look it up normally
Some suggestions:
It's quite unusual to use #Remote annotation on interface and implementation at the same time. Remove this annotation from your stateless bean, it's redundant.
I agree with other posters:
don't use remote interface for local lookups and injections, create another interface and mark it with #Local.
when it comes to JVM-local operations #EJB injection is vastly supperior comparing to manual JNDI lookup (see responses to http://stackoverflow.com/questions/12681436/ejb-annotation-vs-jndi-lookup and similar questions on SO).
To sum it all:
create OperationPerformedLocal.java :
#Local
public interface OperationPerformedLocal {
public void addRandomNo(String randomNos);
}
modify your SLSB:
#Stateless
public class OperationPerformedImpl implements OperationPerformedLocal, OperationPerformed {
public void addRandomNo(String randomNos) {
}
}
and use injection rather than jndi lookup whenever possible:
#EJB
private OperationPerformedLocal operationPerformed;
In case of WAR modules some CDI #Injection magic should work, but I haven't got any real experience with it yet.
Now, if you insist on using plain JNDI lookups and want them to be as generic as possible, I came up with this solution:
public class BeanUtil {
private static InitialContext ctx;
public static <T, C> T create(Class<C> implType, Class<T> interfaceType) {
try {
ctx = new InitialContext();
return (T)ctx.lookup(implType.getSimpleName()+"/local");
} catch (NamingException e) {
e.printStackTrace();
}
return null;
}
}
you can use it this way:
try {
OperationPerformedLocal operationPerformed =
BeanUtil.create(OperationPerformedImpl.class, OperationPerformedLocal.class);
operationPerformed.addRandomNo("123");
} catch (Exception e) {
e.printStackTrace();
}
It's ugly I know, I'm kind of embarassed posting it.
It's not portable, forces you to know interface and implementation classes and "/local" part is hardcoded making it even more ugly ::sigh::.
Placing EJB-JAR(s) into EAR can complicate things a little bit further, jndi-name will be prefixed with EAR name:
return (T)ctx.lookup("earName/" + implType.getSimpleName() + "/local");
Moreover, it'll probably break if SLSB has changed name i.e. #Stateless(name="someNewName").