Is it possible (if so, how) would one add custom methods in the CrudRepository interface?
I have the following DAO with a entity class called, Foobar. For the sake of this question - the entity class can have any number of fields/columns.
package com.bar
import org.springframework.data.repository.CrudRepository;
import com.bar.Foobar
public interface FoobarCrudRepo extends CrudRepository<Foobar, Long> {
public Foobar findByObject(Foobar JObj);
}
This code works fine if you remove that custom method but throws an error if I add it (like above). My understanding from this post (Update or SaveorUpdate in CRUDRespository, Is there any options available) was that I just needed to add an additional method to the interface (as shown in the example) and the JPARepo will take care of the rest. However, I'm getting the error below. My thinking - it doesn't know much about this custom class.
Here's my error:
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'Foobar': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property object found for type Foobar!
Why would I want a findbyObject() you might ask? So that I don't insert duplicate rows of data into the DB by checking the DB to see if the object has already been added and performing a check.
Any suggestions would help, thanks.
FindByObject doesn't look for an object, it literally looks for a field in your Foobar class named Object. Like others have mentioned, you can check other fields of the object to see if it already exists.
For example, if your Foobar class has String name and String email fields, you could create a method like public Foobar findByNameAndEmail(String name, String email) {}, and this would return a Foobar object if there was a record in the database that met these criteria.
All query methods do is replicate SQL, so ask yourself, could you "find by object (record)" in SQL? No, because that makes no sense. Your query would return a record (object) WHERE certain conditions are met.
Your code does not work because it takes the Names behind findBy and tries to create the where condition with that.
So findByObject creates:
where object = ?
to find the object by id you can call
findOne(?)
Related
I would like to create a central logger class that logs messages like:
Couldn't find a record of type MyClass when searching for field
MyField
Currently, I have this piece of code:
public static <T, A, B> String logNotFound(final Class<T> type, String field) {
return String.format(DATA_NOT_FOUND, type.getSimpleName(), field);
}
And I call it like:
Optional<Person> person = findPersonByLastName("Smith");
if (person.isEmpty()) logNotFound(Person.class, "lastName");
However, I don't quite like passing a string to the field name. I would like to call the log method as
logNotFound(Person.class, Person::getLastName)
passing a Function<A,B> as parameter. I expect a message like
Couldn't find a record of type Person when searching for field > Person::getLastName
Is there a way to do this?
seems to be XY problem: you actually need to log errors in convenient way, but do not know how to refer class fields in code... There are two options:
lombok: #FieldNameConstants - there are some issues
implement annotation processor which will generate metamodel for your classes (or if you are on HBN you may use existing one: https://vladmihalcea.com/jpa-criteria-metamodel/)
It is possible, use (or analyze the source code and implement it in your way) safety-mirror library:
assertEquals("isEmpty", Fun.getName(String::isEmpty));
Here you read more details - Printing debug info on errors with java 8 lambda expressions
I am trying to get the total number (count) for a given field from an index using a generic repository. The index mapping is huge and I do not wish to have an equivalent mapping on the Springboot side. The query is extremely simple;
http://localhost:9203/type_index/_count?q=person.name:john;
Should I have a generic class for the repository?
Can I use the #Query annotation?
How do I define which type and index that the repository should go to? Normally, this is done by the #Document(indexName="type_index) annotation on the Entity class but in this case, I will not have one.
#Repository
public interface GenericElasticsearchRepository<T, ID> extends ElasticsearchRepository<T, ID> {
#Query("{\"bool\": {\"must\": [{\"match\": {\"person.name\": \"?0\"}}]}}")
long countByName(String name);
}
When I use the above code, I am getting the following error;
ElasticSearchConfiguration: Invocation of init method failed;
nested exception is org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class java.lang.Object!
Thanks!
As for building a query method without having the entity you could go for a custom implementation (https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#repositories.single-repository-behavior).
Define an interface (not extending ElasticsearchRepository) like
interface CountByNameRepository {
long countByName(String name);
}
and an implementation (just typing this here, not using an IDE, ao there might be errors):
public class CountByNameRepositoryImpl {
private final ElasticsearchOperations operations;
public CountByNameRepositoryImpl(ElasticsearchOperations operations) {
this.operations = operations;
}
public long countByName(String name) {
Query query = new CriteriaQuery(Criteria.where("person.name").is(name));
return operations.count(query, IndexCoordinates.of("type-index"));
}
}
Your respository then will need to extend bot ElasticsearchRepository and CountByNameRepository, Spring Data Elasticsearch will use the provided implementation.
I have the index name hard coded here, as I don't know where you have access to this. You might be able to pass it as a second parameter to the method, but this depends on your concrete use case.
I have the following case:
I have class with a field and a Annotation:
public class TestClass{
#Property
private Object testValue;
}
My goal is to create a Method for each annotated field that would create a "boilerplate code in a lombok like fashion" for the Property for each Value that is annotated that should look like this
public Object getTestValueProperty{
return testValue;
}
I can access the fields over reflection and anotations, And read the Type etc. But there is no Way to "Create" a Method Structure and adding it to the class on runtime.
I looked into the following: http://notatube.blogspot.com/2010/12/project-lombok-creating-custom.html
But I failed to get access the JavacAnnotationHandler to create the new Method. Since this article is 10 Years Old, I would assume this is not up to date anymore and that there are other ways to "inject" a method into a class.
If you could give me any resources or Ideas how to research this topic I would appriciate it.
I have the following form model:
ReservationLockRequestForm:
public class ReservationLockRequestForm {
private Restaurant restaurant;
private ReservationInquiryResponse reservationData;
private Time reservationTime;
}
I left out the getters, setters and empty constructor for legibility.
Now, If i call this
formFactory.form(ReservationLockRequestForm.class).bindFromRequest().get()
I get
Invalid property 'restaurant[tables][1][numberOfChairs]' of bean class [models.helpers.forms.ReservationLockRequestForm]: Illegal attempt to get property 'restaurant' threw exception
The Restaurant Model contains a List<Tables> object, and the Tables model does contain a numberOfChairs property.
What am I doing wrong?
EDIT: Adding a Breakpoint in the ReservationLockRequestForm Restataurant Setter revels that the incoming Restaurant object is empty (all properties are null), but a quick check of the request revels that it contains all the data.
You need a custom property editor. You'll need to have a class which extends PropertyEditorSupport e.g. RestaurantPropertyEditor extends PropertyEditorSupport, which is responsible for converting restaurant, or whichever field to and from text representation. It will need to override setAsText and getAsText.
Then in your controller which returns the view, you will need to have
#InitBinder("reservationLockRequestForm ")
public void initBinder(WebDataBinder binder)
{
binder.registerCustomEditor(Restaurant .class, new RestaurantPropertyEditor());
// ... other ones too
}
Often your PropertyEditor will need your dao to convert from an id to an entity and vice versa, you'll need to do all this yourself.
Sometimes it is easier not to use Spring Binding directly with your entity and manually handling the request parameters from the post/get. Keep that in mind, I find this is the case for dealing with parameters which are collections.
Okay I fixed it. Turns out the Front-End was sending the request as Form-Data, not as JSON. I changed that, and without any modifications to the Back-End it worked flawlessly
If I take a service method named public void delete(int id); as the pointcut, I want to add an after-returning advice on it, but I don't know what kind of object was deleted(however, the servlet which called the service knows the type value), so I was wondering if I can pass a customized value to this after-returning advice when it is activated, like 'user'. I've already checked the related document on Spring's website and I still know nothing. I'd appreciate your answer, THX.
One solution but its required refactoring in Service method
1) Refactoring
public class DeleteRequest {
String type;
Long id;
}
public boolean delete(DeleteRequest request){ // impl}
2) Use Around Advice
2.1) Before proceeding method execution, read passed parameter & get to be deleted object for "XYZ" requirement.
2.2) Capture result of delete method execution
IF its TRUE then DO your stuff
Note: I used this approach for deleted entity notification. So how we can get deleted entity information in after-advice, hence keep it entity information in before phase & use it in after-successful execution.