I am trying to migrate the application. I am working on from Hibernate to Spring Data Jpa.
Though spring data jpa offers simple methods for query building, I am stuck up in creating query method that uses both And and Or operator.
MethodName - findByPlan_PlanTypeInAndSetupStepIsNullOrStepupStepIs(...)
When it converts into the query, the first two expressions are combined and it executes as [(exp1 and exp2) or (exp3)].
whereas required is ](exp1) and (exp2 or exp3)].
Can anyone please let me know if this is achievable through Spring data jpa?
Agree with Oliver on long and unreadable method names, but nevertheless and for the sake of argument, you can achieve desired result by using the equivalency
A /\ (B \/ C) <=> (A /\ B) \/ (A /\ C)
A and (B or C) <=> (A and B) or (A and C)
So in your case it should look something like this:
findByPlan_PlanTypeInAndSetupStepIsNullOrPlan_PlanTypeInAndStepupStepIs(...)
It's currently not possible and also won't be in the future. I'd argue that even if it was possible, with a more complex query you wouldn't want to artificially squeeze all query complexity into the method name. Not only because it becomes hard to digest what's actually going on in the query but also from a client code point of view: you want to use expressive method names, which — in case of a simple findByUsername(…) — the query derivation allows you to create.
For more complex stuff you' just elevate query complexity into the calling code and it's advisable to rather move to a readable method name that semantically expresses what the query does and keep the query complexity in a manually declared query either using #Query, named queries or the like.
Use something like
findByFirstElementAndCriteriaOrSecondElementAndCriteria
is like (first & condition) OR ( second & condition) --> condition & ( first or second)
Option1: You could use named-queries (see Using JPA Named Queries):
#Entity
#NamedQuery(name = "User.findByEmailAddress",
query = "select u from User u where u.emailAddress = ?1")
public class User {
}
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmailAddress(String emailAddress);
}
Option2: use #Query to write your custom queries (see Using #Query)
public interface UserRepository extends JpaRepository<User, Long> {
#Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);
}
Related
I'm trying to utilize a registered function from my custom hibernate dialect inside of a formula field. The problem is that the formula field does not utilize my registered function. I'm trying to figure out what I'm doing wrong.
For background, I have an application that I'm working to make functional for both Oracle and Postgresql. Not simultaneously, but for whichever database its being deployed to. I have several formula fields in my models that are used to aggregate the names of OneToMany mapped entities into a single comma-delimited list for easy searching and display. This was done utilizing LISTAGG when it was purely Oracle. That won't work in Postgresql, but given that it needs to work with both environments, I can't just change the syntax of my Formula to STRING_AGG. So, I'm attempting to register a function for both that will utilize the appropriate format for whichever database is being used.
I'm using Custom Dialect extensions and registering my functions, but it doesn't utilize my registered function. I'm not sure what I'm doing wrong.
If this isn't actually possible and I'm approaching this from the wrong direction, is there a good approach to defining the formula fields dynamically? Not during runtime, but during compile time when the dialect is set?
public class CustomPostgresqlDialect extends PostgreSQL95Dialect {
public CustomPostgresqlDialect() {
super();
registerFunction("MY_LISTAGG", new SQLFunctionTemplate( StandardBasicTypes.STRING, " STRING_AGG(?1 , ', ' ORDER BY ?1) "));
}
}
...
public class CustomOracleDialect extends Oracle12cDialect {
public CustomOracleDialect() {
super();
registerKeyword("WITHIN");
registerFunction("MY_LISTAGG", new SQLFunctionTemplate( StandardBasicTypes.STRING,"LISTAGG(?1,', ') WITHIN GROUP(ORDER BY ?1)"));
}
}
And here is my model with the formula:
public class Contractor extends Object implements Serializable {
...
#OneToMany(mappedBy = "contractor", fetch = FetchType.LAZY)
private Set<ProjectManager> projectManagers;
...
#Formula("(" +
"SELECT\r\n" +
" MY_LISTAGG(PM.NAME)\r\n" +
"FROM\r\n" +
" PROJECTMANAGERS PM\r\n" +
" INNER JOIN CONTRACTORS C ON C.ID = PM.FK_CONTRACTOR\r\n" +
"WHERE\r\n" +
" C.ID = id" +
")"
)
#NotAudited
private String pmNames;
...
}
Like SternK wrote, it's not possible to use JPQL/HQL functions in #Formula and I would also advise against using subqueries in formulas in general as that will incur the penalty of always executing these subqueries even if you don't need the data.
I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Contractor.class)
public interface ContractorDto {
#IdMapping
Long getId();
#Mapping(value = "projectManagers.name", fetch = MULTISET)
Set<String> getPmNames();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ContractorDto a = entityViewManager.find(entityManager, ContractorDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<ContractorDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary! So in this case, a SQL query like the following would be created:
select
c.id,
(
select json_agg(json_object(c1, pm.name))
from project_manager pm
where pm.fk_contractor = c.id
)
from contractor c
If you really want an aggregated string, you could also use the GROUP_CONCAT function as provided by Blaze-Persistence within the mapping:
#EntityView(Contractor.class)
public interface ContractorDto {
#IdMapping
Long getId();
#Mapping("GROUP_CONCAT(projectManagers.name, 'SEPARATOR', ', ', 'ORDER BY', projectManagers.name,, 'ASC')")
String getPmNames();
}
You can not use #Formula for this purpose. As it's stated in the documentation:
You should be aware that the #Formula annotation takes a native SQL clause which may affect database portability.
You can try to use JPQL/Criteria query for this.
I was wondering if there was a way using JPA query (not sure what the word I'm looking for is sorry).
#Transactional
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByLastNameIgnoreCase(String lastName); //This is the format I am looking for
#Query("SELECT DISTINCT t.lastName FROM User t") //Don't want to have to use the #Query
List<String> findDistinctLastNames();
}
Hopefully that makes it more clear. But I am trying to perform that Query without having to use the #Query. It doesn't really affect anything having it there, I would just like it. Is that statement possible?
Spring Data JPA uses reflection to match method signatures to field names and operations so confirming you have the correct column name would be a good place to start, i.e. is it lastName or lastNames?
Otherwise, according to the Spring Data JPA documentation, the following should work correctly:
List<User> findDistinctByLastName(String lastName);
Are you getting a stack trace?
I am trying to migrate the application. I am working on from Hibernate to Spring Data Jpa.
Though spring data jpa offers simple methods for query building, I am stuck up in creating query method that uses both And and Or operator.
MethodName - findByPlan_PlanTypeInAndSetupStepIsNullOrStepupStepIs(...)
When it converts into the query, the first two expressions are combined and it executes as [(exp1 and exp2) or (exp3)].
whereas required is ](exp1) and (exp2 or exp3)].
Can anyone please let me know if this is achievable through Spring data jpa?
Agree with Oliver on long and unreadable method names, but nevertheless and for the sake of argument, you can achieve desired result by using the equivalency
A /\ (B \/ C) <=> (A /\ B) \/ (A /\ C)
A and (B or C) <=> (A and B) or (A and C)
So in your case it should look something like this:
findByPlan_PlanTypeInAndSetupStepIsNullOrPlan_PlanTypeInAndStepupStepIs(...)
It's currently not possible and also won't be in the future. I'd argue that even if it was possible, with a more complex query you wouldn't want to artificially squeeze all query complexity into the method name. Not only because it becomes hard to digest what's actually going on in the query but also from a client code point of view: you want to use expressive method names, which — in case of a simple findByUsername(…) — the query derivation allows you to create.
For more complex stuff you' just elevate query complexity into the calling code and it's advisable to rather move to a readable method name that semantically expresses what the query does and keep the query complexity in a manually declared query either using #Query, named queries or the like.
Use something like
findByFirstElementAndCriteriaOrSecondElementAndCriteria
is like (first & condition) OR ( second & condition) --> condition & ( first or second)
Option1: You could use named-queries (see Using JPA Named Queries):
#Entity
#NamedQuery(name = "User.findByEmailAddress",
query = "select u from User u where u.emailAddress = ?1")
public class User {
}
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmailAddress(String emailAddress);
}
Option2: use #Query to write your custom queries (see Using #Query)
public interface UserRepository extends JpaRepository<User, Long> {
#Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);
}
We have an SQL statement which is executed by Jdbi (org.skife.jdbi.v2). For binding parameters we use Jdbi's bind method:
Handle handle = ...
Query<Map<String, Object>> sqlQuery = handle.createQuery(query);
sqlQuery.bind(...)
However we have a problem with in-lists and currently we are using String.format for this. So our query can look like this:
SELECT DISTINCT
tableOne.columnOne,
tableTwo.columnTwo,
tableTwo.columnThree
FROM tableOne
JOIN tableTwo
ON tableOne.columnOne = tableTwo.columnOne
WHERE tableTwo.columnTwo = :parameterOne
AND tableTwo.columnThree IN (%s)
%s is replaced by String.format so we have to generate a proper string in java code. Then after all %s are replaced we are using jdbi's bind method to replace all other parameters (:parameterOne or ?).
Is there a way to replace String.format with jdbi? There is a method bind(String, Object) but it doesn't handle lists/arrays by default. I have found this article which explains how to write our own factory for binding custom objects but it looks like a lot of effort, especially for something that should be already supported.
The article you linked also descibes the #BindIn annotation. This provides a general purpose implementiation for lists.
#UseStringTemplate3StatementLocator
public class MyQuery {
#SqlQuery("select id from foo where name in (<nameList>)")
List<Integer> getIds(#BindIn("nameList") List<String> nameList);
}
Please note that you'll have to escape all pointy brackets < like this \\<. There is a previous discusion on SO: How to do in-query in jDBI?
I just wanted to add an example since I recently spent considerable time getting a slightly more complex scenario to work :
Query :
select * from sometable where id <:id and keys in (<keys>)
What worked for me :
#UseStringTemplate3StatementLocator
public interface someDAO {
....
....
// This is the method that uses BindIn
#Mapper(someClassMapper.class)
#SqlQuery("select something from sometable where age \\< :age and name in (<names>)")
List<someclass> someMethod (#Bind("age") long age, #BindIn("names") List<string> names);
#Mapper(someClassMapper.class)
#SqlQuery("select something from sometable where id = :id")
List<someclass> someMethod1 (#Bind("id") long id);
...
...
}
Note: I did have to also add the below dependency since I am using
#UseStringTemplate3StatementLocator
<dependency>
<groupId>org.antlr</groupId>
<artifactId>stringtemplate</artifactId>
<version>3.2.1</version>
</dependency>
The main thing to observe in the above example : You only need to escape the less than operator (i.e. < ) and not the <> that surround the collection variable (names).
As you can see I did not use a sql.stg file to write my queries in. Initially I incorrectly assumed that when using #UseStringTemplate3StatementLocator , we have to write the queries in the sql.stg file. However, somehow I never got my sql.stg file to work and I eventually reverted back to writing the query within the DAO class using #SqlQuery.
For the most recent jdbi version things got easier:
public interface MyDao {
#SqlQuery("select id from foo where name in (<nameList>)")
List<Integer> getIds(#BindList("nameList") List<String> nameList);
}
#BindIn → #BindList, and no longer requires StringTemplate
Reference: https://jdbi.org/
I'm using Java - Ibatis and mySQL with Flex/Flash on the front-end. I have one requirement that is to be able to dynamically add creterias and table to a query depending on the user role. here is an example
Same object calling same SQL but different result based on role
Role 1 : Fully Access to employees
SELECT *
FROM Employee A
Role 2 : Limited access to employees
SELECT *
FROM Employee A
, SECURE_LIST B
WHERE B.EmployeeID = A.EmployeeID
AND B.ROLE_ID = 'ROLE'
I could use Dynamic SQL
SELECT *
FROM Employee A
<isNotEmpty property="ROLE" >
, SECURE_LIST B
WHERE B.EmployeeID = A.EmployeeID
AND B.ROLE_ID = #ROLE#
</isNotEmpty>
Other ideas?
SELECT *
FROM Employee A
<isNotEmpty property="ROLE" >
inner join SECURE_LIST B on B.EmployeeID = A.EmployeeID
</isNotEmpty>
<dynamic prepend="WHERE">
<isNotEmpty property="ROLE" prepend="AND">
B.ROLE_ID = #ROLE#
</isNotEmpty>
</dynamic>
A little simpler than creating DAOs but still providing you the flexibility to add other joins or other where clause elements without having to include role in every parameter map
The problem with using role within the query is that you have to then supply it as an argument to the query for possibly every query. What happens when you need to supply arguments to the query? You'll need to add role to those parameter classes/maps too. It's all a bit messy.
I'd take a step back and define your DAO:
public interface MyDAO {
List<Employee> getEmployees();
...
}
and then create two implementations:
public class MyDAOSuper implements MyDAO {
public List<Employee> getEmployees() {
// call a query using your first SQL
}
}
public class MyDAOLimited implements MyDAO {
public List<Employee> getEmployees() {
// limited version
}
}
One advantage of this approach is that if certain methods shouldn't be used by a particular role you have the option of throwing some security violation exception.
Now how you plug that in to the rest of your application is something I don't have enough detail to comment on. You might be using BlazeDS in which case I'd suggest using the Spring integration with BlazeDS, which will open up dependency injection as an option.
Alternatively you could use a simple factory method (based on role) to get the correct DAO.
There are no doubt other ways to plug this in depending on your configuration. I think the above is a lot cleaner than what you're proposing though.