I'm trying to make a method in custom repository with example query:
#Query("select e from Entity e where :#{#customService.myWhereLogic(#e) is not null}")
where customService.myWhereLogic() this my spring bean whith some method. I need to argument myWhereLogic() method's is not null when execute query. How do it(now null, but result query not 0 count)?
Can anybody help me with this?
First, maybe you can enhance your post a little because it's hard to figure out what the question is about. Anyway, I will try to answer.
Invoking methods on passed query parameter objects via SpEL is possible in general, but for multiple reasons it's not a good idea in this case.
It seems your intention is that the service method should somehow replace a where clause, right? But what should it return to satisfy the SQL syntax? Even it would return a column name / value combination or similar to create valid statement, that won't work, because prepared statement parameters are only allowed for column values, neither for columns nor tables. That can only be achieved via plain string concatenation, but that's in general a bad idea within SQL statements.
Your approach would return an invalid SQL statement like:
select e from Entity e where 'yourResult'
And even if it would work, coupling your query and the service like this feels not right.
If you really want to "enhance" your Repositories with business code, try to implement that with Specifications like:
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;
import com.example.model.YourEntity;
import com.example.service.YourService;
#Component
public class SomeSpecification implements Specification<YourEntity> {
#Autowired
private YourService yourService;
#Override
public Predicate toPredicate(final Root<YourEntity> yourEntity, final CriteriaQuery<?> query, final CriteriaBuilder builder) {
return null; // implement your business checks with yourService and some matchers
}
}
Related
Is it possible to extend DbFunctions, assuming you are using existing DbFunctions as helper methods. I am essentially rewriting the exact same line of sql code again and again. Are there any alternatives?
Update:
Here is an example of what I'm trying to do, but I want to define my own Add function, not use one that I've constructed in my database
var locations = context.Data.Where(e => Functions.Add(e.X, e.Y) >= 10)
Yes of course you can extend it. Class DbFunctions only contains code that helps the provider of your IQueryable to translate the expression into SQL.
An IEnumerable holds all code to create an Enumerator for you upon request. The Enumerator represents a sequence. Upon request it will give you the first element of the sequence, and once you've got an element it will give you the next one (provided there is one).
An IQueryable works differently. Usually an IQueryable is not meant to be performed by your process, but for instance a database, a remote web site, a CSV file controller, etc.
That is why you need to tell an object that produces IQueryables for which process it must create the IQueryable. In case of Entity Framework you inform the DbContext which database to use.
The IQueryable object holds an Expression to be performed and a Provider. The provider knows which process will perform the query. It also knows how to translate the Expression into the format that the other process understands. Quite often this will be SQL.
If you investigate the remarks section of the MSDN descriptions of IQueryable functions like Where, GroupBy, Select, you'll see that most of these functions will only change the Expression.
As long as you don't ask for the Enumerator, usually implicitly by asking for the first element of a sequence, like in ToList, foreach, FirstOrDefault, etc, the Provider has nothing to do.
But once you ask for the Enumerator, the Expression will be translated by the Provider, who will use the translation to query the data from the other process and create an Enumerator object that can give you the first element of the sequence, and the next ones.
DbFunctions are used when the Provider translates the Expression into SQL. If you create a Queryable with DbFunctions and in your debugger look at the created Expression, you'll still find the used DbFunctions.
The DbFunctions only translate the input into SQL. If does not perform the query itself. The translation is done in local memory.
Having understood this, you can use any function as long as it only changes the Expression into new Expressions into formats that your provider understands.
This means you can't use any of your own functions, or classes. There are even several LINQ functions you can't use
See supported and non-supported LINQ methods
However, if your extension functions input an IQueryable and output an IQueryable, then your extension function will only change the Expression. As long as you fill the Expression with supported LINQ methods you're fine
So if you want to extend IQueryable with a function that returns an IQueryable containing only the invoice that are due to day:
public static IQueryable<Invoice> WhereDueToday(this IQueryable<Invoice> invoices)
{ // returns all invoices that must be paid today
return invoices
.Where(invoice => DbFunctions.TruncateTime(invoice.DueDate) == DateTime.Today);
}
Usage:
IQueryable<Invoice> invoices = dbContext.Invoices
.Where(invoice => ..);
IQueryable<Invoice> invoicesDueToDay = invoices
.WhereDueToday();
You can define a method that returns an Expression and use that in your where clause. Since you want to pass different properties of the object in you can't just write one expressions
public Expression<Func<T, bool>> MyFunc<T>(Expression<T, int> property1, Expression<T, int> property2, int greaterThan)
{
// Build expression tree
}
I realise "Build Expression tree" isn't hugely useful, but if you don't really want to do add writing out the code to build "add" isn't going to help you either.
If there are just a couple of combinations it might be easier to just hard code for those
public Expression<Func<T, bool>> MyFunc<T>(PropertiesEnum p, int greaterThan)
{
switch(p)
{
case (p.XandY):
return item => (item.X + item.Y) > greaterThan;
case (p.XandZ):
return item => (item.X + item.Z) > greaterThan;
case (p.YandZ):
return item => (item.X + item.Z) > greaterThan;
// other cases
}
}
You would call this like:
var locations = context.Data.Where(MyFunc(PropertiesEnum.XandY, 10));
I would like to use the Spring Data Projection technique in order to extract from a table only some fields (and not all fields of the table).
As described in the documentation (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections) I created a simple interface, for example:
interface NamesOnly {
String getFirstname();
String getLastname();
}
But I have some problems to use it.
Problem 1:
First of all, I would like to use the name findAll() to create a query that finds all rows with only two fields (firstName and lastName):
#Repository
public interface PersonaRepository extends JpaRepository<Persona, Long> {
List<NamesOnly> findAll();
}
But in this case I have these errors (maybe because findAll() is a method of the JpaRepository):
implements org.springframework.data.jpa.repository.JpaRepository.findAll
The return type is incompatible with JpaRepository.findAll()
Problem 2:
Ok, so I try to change the name of the method to findAllOnlyNames():
#Repository
public interface PersonaRepository extends JpaRepository<Persona, Long> {
List<NamesOnly> findAllOnlyNames();
}
But now I have this error:
Caused by:
org.springframework.data.mapping.PropertyReferenceException: No
property findAllOnlyNames found for type Persona!
Because Spring tries to create a query from the name.
1) Could it be possible to reuse the method name findAll() without having problems with JpaRepository?
2) Could it be possible to turn off the query creation from the method name (only for some queries, not for all projects or repositories)?
You are on the right track, your findAll() is in conflict with the ones specified on the existing Spring Data interfaces and you can rename it (as you tried) but it still has to be a name that is compatible with the query derivation mechanism. Try this instead:
#Repository
public interface PersonaRepository extends JpaRepository<Persona, Long> {
List<NamesOnly> findAllOnlyNamesBy();
}
This part of the Spring Data JPA documentation explains how the query creation process works:
The mechanism strips the prefixes find…By, read…By, query…By, count…By, and get…By from the method and starts parsing the rest of it.
So you just need to add the By keyword in the method name, anything after that keyword is treated as a condition, in this case there is no condition so it fetches everything.
To disable the query derivation from the method name you would need to add an #Query(...) annotation to the method and specify either a JPA or native query instead.
You can specify an explicit query rather than rely on it being derived from the method name.
#Repository
public interface PersonaRepository extends JpaRepository<Persona, Long> {
#Query("select p from Persona p")
List<NamesOnly> findAllOnlyNames();
}
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query
Overriding findAll() (even in the unlikely event it is possible) is probably a bad idea.
I have some check constraints defined by SYS schema in DB for a particular column.
Now, while invoking it from Java code through MyBatis, is there anyway to enforce corresponding field length validations through MYBatis configuration only.
PS: I don't want to enforce constraints at VO level (setter individually). Or using JSR 303
DataBase : Oracle 11g
Using MyBatis
If you do not want to validate in your java beans (manually, or using JSR 303) I think you could write your own typeHandler for those field.
Typehandler would handle String fields and do validation.
See code example for String TypeHandler.
You could enforce your validation logic (of any complexity) in handler's get/set methods.
If you want to use TypeHandler to trim string to given length when saving to database, do it in setNonNullParameter method.
Sample code below
#MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {
#Override
public void setNonNullParameter(PreparedStatement ps, int i,
String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter.substring(0,DESIRED_MAX_LENGTH));
}
You could also trim (or otherwise modify) values you read from database- you need to modify get* method in your TypeHandler implementation to do that.
You must tell mappers to use your handler. Otherwise, default handler for given type will be used.
Your SQLs in XML file must use syntax
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
Check https://mybatis.github.io/mybatis-3/sqlmap-xml.html for details.
Let me say, I have come to the conclusion (after a lot of trial) that Repository & Unit of Work when using Entity Framework is just wrong, wrong, wrong and this says why quite well.
But I really hate on those embedded queries. Question is, where can I put them instead if I'm so against a repository, etc? (clean answers only please, examples much appreciated).
I just nuked two projects containing my repositories, unit of work and interfaces with hundreds of files because the payback was nowhere to be seen. I think lots of people, myself included, just jumped on the Repository bandwagon because that's what everybody else was doing but in retrospect, I think it's really a ride to nowhere.
/sigh
Richard
Where do you expect to put them? You have only few choices:
Let them be where they are and use custom extension methods, query views, mapped database views or custom defining queries to define reusable parts
Expose every single query as method on some separate class. The method mustn't expose IQueryable and mustn't accept Expression as parameter = whole query logic must be wrapped in the method. But this will make your class covering related methods much like repository (the only one which can be mocked or faked). This implementation is close to implementation used with stored procedures.
You will do the same as in previous method but instead of placing queries in separate class you will put them as static methods to entity directly. This is much worse testable because static methods cannot be replaced by mocking (it requires more complex testing framework). This is part of active record pattern where each entity is responsible for its loading and saving to database.
Example of custom extension method:
public static IQueryable<TEntity> GetByName(this IQueryalbe<TEntity> query, string name)
where TEntity : IEntityWithName
{
return query.Where(e => e.Name == name);
}
Example of custom class exposing methods:
public class QueryProvider
{
public QueryProvider() {}
public IEnumerable<TEntity> GetByName(IYourContext context, string name)
where TEntity : IEntityWithName
{
return context.CreateObjectSet<TEntity>().Where(e => e.Name == name).ToList();
}
}
Build Reusable, Testable Queries Part 1
This is a blog post I wrote about building reusable queries. Using Extension Methods allows you to build composable queries.
using a pattern like the specification pattern can help you build queries that can be reused or saved (serialized). Further more if you have a double entry system you can execute the same query instance over two different databases.
the following example does not use EF but replace the IEnumerable by an EF context and you get what ou are looking for. parameters are passed in through the constructor.
public class PartialMatchQuery : IModelQuery<string, IEnumerable<string>>
{
private readonly string partial;
public PartialMatchQuery(string partialString)
{
partial = partialString;
}
public IEnumerable<string> Execute(IEnumerable<string> model)
{
return model.Where(s => s.ToLower().Contains(partial));
}
}
Basically I'd be looking to implement a method like this.
IQueryAble GetQuery<T>(Entities db) or extension method Entities.GetQuery<T>()
This way you could do things like this
public IQueryable<T> GetAll()
{
return yourEntityClasses.GetQuery<T>();
}
which would return a SELECT * FROM query expression and obviously from there you could make addtional generic methods for sorting, pagination, where expressions, etc on top of this would save you from having to repeat the code for these methods for each table. I know SubSonic3 does a very good job of this, but was trying to duplicate some of the functionality in an EntityFramework project I am working on. Only thing I see in EF is CreateQuery and ObjectQuery but both of those require you to pass a querystring in which would require you to know the table name.
Well it is possible to get the string that you need to put into the CreateQuery method automatically.
Basically it is just string.Format("[{0}]",entitySetName);
How do you get the entity set name, assuming you have don't use something called MEST, most people don't, you can use some a functions I wrote in this Tip to get the EntitySet for T and from that the name.
Once you do that it should be pretty trivial for you to write the Extension Method
i.e.
public static IQueryable<T> GetAll<T>(this ObjectContext context)
{
var wkspace = context.MetadataWorkspace;
EntitySet set = wkspace
.GetEntitySets(wkspace.GetCSpaceEntityType<T>())
.Single();
return context.CreateQuery<T>(string.Format("[{0}]",set.Name);
}
See the above tip for the other functions used.
Hope this helps
Alex