How Do I Write A "Count By" Method Name Query In Spring Data - spring-data

So I know that I can write an interface like the one below and Spring Data will automatically generate the necessary database access stuff for me. Now what I'd like to do is add a new method name that will count the number of entities that match a set of criteria.
public interface EventRegistrationRepository extends JpaRepository<EventRegistration, String>
{
List<EventRegistration> findByUser_EmailAddress(String email);
int countByEvent_Code(String eventCode);
}
As of now the countBy method causes this error:
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property count found for type com.brazencareerist.brazenconnect.model.relational.EventRegistration
What's the proper syntax for what I'm trying to do here?

This works as expected as of the just released Spring Data JPA 1.4.0.M1.

Related

Cannot read property 'collectionName' of undefined when filtering hasMany relationship

I'm using adonis.js/lucid ^6.1.3 with lucid-mongo ^3.1.5 library to manage mongodb database. I need to build a query and filter a hasMany relationship but it throws an exception Cannot read property 'collectionName' of undefined
I've created a model called User and then provided a relation called session which must return all user sessions (Mobile or web session). I've used the approach described in adonis.js lucid relationship for querying data of relationships
return await user.sessions().where({
'logged_in_at': { $type: 10 },
'logged_out_at': { $type: 10 }
}).where('tries', '<', 3).firstOrFail();
The expected output should be a session document (row) but this is the exception it throws Cannot read property 'collectionName' of undefined
firstOrFail is a static method, which I believe means that you cannot use it with the query builder (I could be wrong). Instead, I'd try something along the lines of
return await user.sessions().where({
...
}).andWhere('tries', '<', 3').first()
first is a helper method on the query builder and you have to call fetch when using the query builder with lucid models.
EDIT: for some reason in the Adonis docs, it labels find and first/firstOrFail as static but they are not. reference

How to pass in SQLServerDataTable object to JPA native query

I am trying to pass in a SQLServerDataTable as a Parameter to a JPA native Query.
#Query(value = "Select u FROM #{#entityName} u with (nolock) INNER JOIN :listTable pt on pt.PhoneNumber = #{#entityName}.PhoneNumber WHERE EntityType = :entityType", nativeQuery = true)
Collection<Result> findAllByEntityList(#Param("listTable") SQLServerDataTable listTable, #Param("entityType") Integer entityType);
I get the following exception:
nested exception is org.hibernate.HibernateException: Could not determine a type for class: com.microsoft.sqlserver.jdbc.SQLServerDataTable
How do I resolve this error Please ?
Looks like the Spring JPA/Hibernate current implementation does not yet support passing in SQLServerDataTable as a Param.
I got around this by doing the implementation at the JDBC layer.
In addition to #Brian Antao's answer above there's some nice code for a JDBC implementation here
And if you'd like to inject a Spring datasource bean you can read how to configure it here
Which will provide easy access to your datasource, like so:
SQLServerPreparedStatement statement = (SQLServerPreparedStatement) dataSource.getConnection().prepareStatement(ececStoredProc)

Spring Data JPQL query: lower case for List param

I use Spring Data REST in my project and I have a #Query tag, as such:
#Query("from Customer c where lower(c.customerId) = lower(:customerId) and lower(c.department.businessUnit.name) = lower(:businessUnit)")
List<Customer> findByCustomerIdAndBusinessUnit(#Param('customerId') String customerId, #Param('businessUnit') String businessUnit)
This works perfectly fine. But, I am not sure how to convert to lowercase when a List is passed in, such as:
#Query("SELECT c FROM Customer c WHERE LOWER(c.customerId) IN LOWER(:customerIds)")
Page<Customer> findByCustomerIdIn(#Param("customerIds") List<String> customerIds, Pageable pageable)
That gives me the following error:
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: ( near line 1, column 94 [SELECT c FROM com.myapp.Customer c WHERE LOWER(c.customerId) IN LOWER(:customerIds)]
I understand this happening because Spring Data REST cannot cast a whole list of Strings to lower case (that is not possible in straight up TSQL / PLSQL either!). So, are my options limited to implementing my own repository and build a controller on top of it...? Any better ideas? :)
I believe, you have to transform the list.
Even if you plan to use Spring JPA findByQuery like
List<User> findByCustomerIdInIgnoreCase(List<String> customerIds);
You have to write some code to transform the list into a list of lowercase strings.
If you are looking for converting the list in place, it might not be possible as Strings in Java are immutable
However, if the intention is to do in a single line, with as much less code as possible.
You can do it in Java 8 as below.
List<String> customerIdsLC = customerIds.stream().map(String::toLowerCase).collect(Collectors.toList());
In case, you have a different transformation to do - just provide the right info to map() in the form, Class::method
For e.g.
map(String::toUpperCase) - to convert to Uppercase
map(Long::valueOf) - to convert to Long.

Grails Grom + mongoDb get during save OptimisticLockingException

I try in Grails service save an object to mongodb:
Cover saveCover = new Cover()
saveCover.id = url
saveCover.url = url
saveCover.name = name
saveCover.sku = sku
saveCover.price = price
saveCover.save()
Cover domain looks like this:
class Cover {
String id
String name
String url
String sku
String price
}
So I want to have custom id based on url, but during save process I get error:
Could not commit Datastore transaction; nested exception is
org.grails.datastore.mapping.core.OptimisticLockingException: The
instance was updated by another user while you were editing
But if I didn`t use setters and just pass all values in constructor, the exception is gone. Why?
As reported in the documentation here:
Note that if you manually assign an identifier, then you will need to use the insert method instead of the save method, otherwise GORM can't work out whether you are trying to achieve an insert or an update
so you need to use insert method instead of save when id generator is assigned
cover.insert(failOnError: true)
if you do not define the mapping like this:
static mapping = {
id generator: 'assigned'
}
and will use insert method you'll get an auto-generated objectId:
"_id" : "5496e904e4b03b155725ebdb"
This exception occurs when you assign an id to a new model and try to save it because GORM thinks it should be doing an update.
Why this exception occurs
When I ran into this issue I was using 1.3.0 of the grails-mongo plugin. That uses 1.1.9 of the grails datastore core code. I noticed that the exception gets generated on line 847(ish) of NativeEntryEntityPersister. This code updates an existing domain object in the db.
Above that on line 790 is where isUpdate is created which is used to see if it's an update or not. isInsert is false as it is only true when an insert is forced and readObjectIdentifier will return the id that has been assigned to the object so isUpdate will end up evaluating as true.
Fixing the exception
Thanks to && !isInsert on line 791 if you force an insert the insert code will get called and sure enough the exception will go away. However when I did this the assigned id wasn't saved and instead a generated object id was used. I saw that the fix for this was on line 803 where it checks to see if the generator is set to "assigned".
To fix that you can add the following mapping.
class Cover {
String id
String name
String url
String sku
String price
static mapping = {
id generator: 'assigned'
}
}
A side effect of this is that you will always need to assign an id for new Cover domain objects.

Breeze property validation using data annotations in entity framework

Within my entity framework model I have:
<Required(), Range(0, Double.MaxValue, ErrorMessage:="Weight must be numeric and cannot be negative")> _
Public Property Weight() As Double
<Required(), Range(0, Double.MaxValue, ErrorMessage:="Recycled content must be numeric and between 0 and 100")> _
Public Property RecycledContent() As Double
And in my viewmodel I have:
if (!editComponent().entityAspect.validateProperty("recycledContent")) {
/* do something about errors */
var msg = 'Recycled content is invalid!';
logger.logError(msg, error, system.getModuleId(lt_articleEdit), true);
}
And yet when I enter a value greater than 100 (in the recycled content field) it still passes validation somehow! I have used the script debugger to step through and in the breeze validation routine there are two validators registered which are "required" and "number" but nothing that I can see mentions the range.
Can breeze do range validation? All I'm trying to do is pick up a data validation error based on metadata from the data annotations of the model and use this to trigger a client-side highlight on the field in error and log an error message.
It's a very reasonable request but we aren't quite there yet.
Right now Breeze doesn't YET pick up Range validations from Entity Framework metadata. Please vote for this on the Breeze User Voice . This matters because we do prioritize our work based on this venue.