I'm in the process of migrating a spring boot application to micronaut and stumbled upon a problem with micronaut data.
When using native queries which worked in spring boot data I get a compilation error for a query in which I try to insert some data into associative table.
Unable to implement Repository method: MyEntityRepository.insertQueryExample(int id, String name). No possible implementations found.
Other native queries (selects, deletes) work no problem, same goes for generated methods.
Here's how the repo with said method looks like:
public interface MyEntityRepository extends CrudRepository<MyEntity, Integer> {
#Query(value = "insert into my_entity_my_entity2 (id, id2)" +
" values (:id, (select me2.id from my_entity2 os where me2.name = :name))", nativeQuery = true)
void insertQueryExample(int id, String name);
}
There's no entity class for my_entity_my_entity2 but that worked in spring so I don't think that's a problem.
Thanks in advance for your help.
There's no entity class for my_entity_my_entity2 but that worked in spring so I don't think that's a problem.
Indeed, this is the issue.
All io.micronaut.data.repository.GenericRepository expects a respective entity type (which must be introspected, would it be the Micronaut Data JPA or Micronaut Data JDBC implementation.
The solution you are left with is to implement a custom JpaRepository sub-type and use either the injected EntityManager or JpaRepositoryOperations to perform custom query execution while retaining default intercepted methods:
#Repository
public abstract class MyEntityRepository implements CrudRepository < MyEntity, Integer> {
#Inject
JpaRepositoryOperations operations;
#Transactional
void insertQueryExample(int id, String name) {
operations.getCurrentEntityManager()
.createNativeQuery("insert into my_entity_my_entity2 (id, id2)" +
" values (:id, (select os.id from my_entity2 os where os.name = :name))")
.setParameter("id", id)
.setParameter("name", name)
.executeUpdate();
}
}
You can then inject your MyEntityRepository bean and invoke your custom query method.
Related
I am using Spring Boot application. I am using following method to return List<String> using #Query
annotation.
#Query("select c.name from Customer c")
List<String> findAllNames();
Is there a way to get the above result without #Query or #NativeQuery annotations?
Spring Data JPA supports this quite well. You can define a POJO that represents the (reduced) object (the projection) you want of the entity, then define a method that describes the query, returns the appropriate type, and Spring Data will generate all the code necessary to execute the query and map the results.
Here's an example of a custom repository interface that uses this technique (untested pseudo-code):
public class CustomerSummary {
private String name;
// constructor and getters
}
#Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<CustomerSummary> getAllSummaries();
}
I'm not sure if you can use "simple" types such as String in the return type, but I wouldn't be surprised if that is supported.
Also, you might have to tweak the exact method naming of `` to make sure Spring Data correctly interprets it as you want. Here is some additional reference documentation.
You could use projection to select only name property:
interface CustomerNameProjection {
String getName();
}
and then use it in Repository class:
List<CustomerNameProjection> findAllProjectedBy();
edit: corrected repository method name
Inject EntityManager and call createQuery.
entityManager.createQuery("select c.name from Customer c")
.getResultList()
I have a question about the #Query annotation that I am using with Spring CrudRepository.
Repository example
public interface DataRepository extends CrudRepository<Entity, Long> {
#Query("WITH $param as param.......")
List<Entity> getByQuery(params...);
}
this service is working fine, but the query String is a huge query and I want to keep it in the resources folder as /resources/query/getEntities.cypher
Can someone help me to understand how to load a query from the resources and populate the query string to the #Query annotation. I remember that it's possible with JPA namedQuery, but here I do not have this dependencies
I want this code to be like this:
public interface DataRepository extends CrudRepository<Entity, Long> {
#Query("/resources/query/getEntities.cypher")
List<Entity> getByQuery(params...);
}
I've tried to create a BeanPostProcess with spring and dynamically update the #Query annotation but getting some errors because of it's a Proxy objects everywhere in the Spring beans implementations
I've tried this:
Query query = AnnotationUtils.findAnnotation(method,Query.class);
then I am not able to get the fields from query as it's a proxies
Thanks in advance
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 am developing Spring Boot + Spring Data JPA + Postgres + Lombok example. In this example, I want to fetch all student firstName is ASC order and whose status in Active.
I developed below query, which works fine, but I dont see a way to also use status=Active here in JpaRepository query.
NOTE: In my case, status field is Enum in Java.
Is there any way if we can do the same ? I know I can fetch all students and then using streams can easily filter, but using JpaRepository query, is there any way?
List<Student> students = studentRepository.findAll(new Sort(Sort.Direction.ASC, "studentName"));
In your StudentRepository interface, that extends Repository / JpaRepository you can add a method signature like that:
public interface StudentRepository extends ....{
List<Student> findAllByStatusOrderByStudentNameAsc(String status);
}
Just put the following method signature in your repository and call it where you need with argument 'Active'.
List<Student> findAllByStatusOrderByStudentNameAsc(String status);
In Spring Data is it possible to turn off Query Generation from method names?
Given the interface
public interface UserRepository extends Repository<User, Long> {
List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);
}
I would want spring security to produce an error saying that generating queries from method names has been turned off please use the explicitly #Query annotation like so.
public interface UserRepository extends Repository<User, Long> {
#Query("select u from User u where u.emailAddress = ?1 and u.lastname = ?2")
List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);
}
I want to turn off the the automatic query generation because I think it is easier to read the query and know what is going on rather than reading the method name and translating to what is the query that Spring data will generate, also on a large team with lots of developers some who might not yet be familiar with spring data #Query is a lot more readable?
How to turn off Query creation from method names in Spring JPA?
You can specify the query-lookup-strategy on the repositories tag in the configuration.
<repositories query-lookup-strategy="use-declared-query"/>
See the documentation
User.java
#Entity
#NamedQuery(name="User.findByEmailAddressAndLastName",
query="select u from User u where u.emailAddress = ?1 and u.lastname = ?2")
public User{
}
UserRepository.java
public interface UserRepository extends Repository<User, Long> {
List<User> findByEmailAddressAndLastname(String emailAddress, String lastname);
}
How Spring handles this is called the Query Lookup Strategy. Queries can be resolved by method names (CREATE), by manual queries (USE_DECLARED_QUERY), or both (CREATE_IF_NOT_FOUND) which defaults to method names if no manual query is found. USE_DECLARED_QUERY would give you the desired functionality, warning you if no manual query is specified.
As Kevin answered, this can be configured in xml. But as a more modern option, you can specify the lookup strategy when configuring your repository in a Java Config class with queryLookupStrategy parameter in the #Enable{store}Repositories annotation.
For example, to force manual queries, you could use the following:
#EnableJpaRepositories(queryLookupStrategy=QueryLookupStrategy.Key.USE_DECLARED_QUERY)
public class MyDatabaseConfig {
...
}
More info here
https://docs.spring.io/spring-data/jpa/docs/2.0.7.RELEASE/reference/html/#repositories.query-methods.query-lookup-strategies
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/query/QueryLookupStrategy.Key.html?is-external=true