how to use postgresql array_agg function in JpaRepository spring boot? - spring-boot

I have created new spring boot project with postgresql .I like to use posgressql array_agg(ex:get all department) using JPA Repository native query but its getting some error in blow posted. I have tried some alter solution but cant able to get expected data.
Error :
org.springframework.orm.jpa.JpaSystemException: No Dialect mapping for JDBC type: 2003;
nested exception is org.hibernate.MappingException: No Dialect mapping for JDBC type: 2003
Expected : Should get array or list of data
#Repository
public interface PostGroupRepository extends JpaRepository<PostGroup, Integer> {
#Query(value = "SELECT array_agg(department) FROM boxinfo;", nativeQuery = true)
public Object[] getDept();
}

First solution is to use below dependency:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.11.1</version>
</dependency>
he has custom types already written and register that in the custom dialect like below
public class CustomPostgreDialect extends PostgreSQL10Dialect {
public CustomPostgreDialect() {
super();
this.registerHibernateType(2003, StringArrayType.class.getName());
}
}
And use this dialect as the hibernate dialect in application.yaml or application.properties of spring boot.
spring.jpa.properties.hibernate.dialect: <packageName>.CustomPostgreDialect
Second solution is to write the custom type yourself and register it in the dialect as shown above, if you don't want to use dependency.

Related

Spring boot not able to read custom #Query annotation

I have requirement when we apply #CustomQUery annotation, then I need to intercept this method and append the query predicate which I already know.
Created
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
#Documented
public #interface CustomQuery {
Query query();
}
#Repository
public interface FloorRepository extends JpaRepository<TnFloor, Integer> {
public String query="select distinct tnFloor from TnFloor tnFloor where tnFloor.tnBuilding.buildingId in ?1 ";
#CustomQUery(query=#Query(query))
public List<TnFloor> findByBuildingIds(List<Integer> buildingIds);
}
Here, Spring is unable to read this #CustomQUery because I have not mentioned anywhere to read this annotation.
Is this the correct way to create custom query annotation ?
I am getting below exception on application startup.
Could not create query for public abstract java.util.List
FloorRepository.findByBuildingIds(java.util.List)!
Reason: Failed to create query for method public abstract java.util.List FloorRepository.findByBuildingIds(java.util.List)!
No property buildingIds found for type TnFloor!
Did you mean 'buildingId'?;
nested exception is java.lang.IllegalArgumentException: Failed to c
As other people have already said in comments, I think your way of extending the Query annotation is usefull only if you need to do some things more than just extending it.
If you need some paths to enhance the behavior of the #Query annotation, maybe using #Modifying annotation could get the point, or using #NamedQuery and #NamedNativeQuery annotations too.
If it is a requirement you could not resolve with these other annotations, maybe make some click on them to see how they are declared and raised in the Spring IoC ecosystem using Aspect programming.
The problem here, to my point of view, seems not to be related to your annotation, but a missing property, as the error message has told to you :
No property buildingIds found for type TnFloor! Did you mean 'buildingId'?;
Maybe because of a typo error in your annotation when you are using it, which is not found :
declared as public #interface CustomQuery and used as #CustomQUery(query=#Query(query)). Write the things as they are declared will work better I think.
Did you try using native query feature like that too ?
Query q = em.createNativeQuery("SELECT a.firstname, a.lastname FROM Author a");
// of course, update with your own code.
You can look at what JPA is capable of and switch to native query as I have just added if it is not supported.

Spring Boot 2 + JdbcTemplate - is there a way to provide SQL query for each database supported?

I'm working on a Spring Boot 2.4.2 based project and using "spring-boot-starter-jdbc" and "com.oracle.database.jdbc" for Oracle Jdbc driver.
As I use JdbcTemplate to interact with DB, everything seems clean and easy. But I may need to support multiple database types in future - Oracle, SQL Server, MySQL, DB2, etc.
Did quite a bit of Googling but did not find any option for this..
Like mentioned above, I am using Spring-Jdbc (not Spring Data JDBC or Spring Data JPA) - how do I provide the SQL queries specific to each database supported in the code or configuration?
Please, let me know your thoughts. Thanks.
I'm not familiar with Spring JDBC, but you could user Spring dependency injection mechanism to create a profile for each database.
First an interface:
public interface DbQueries {
String createQueryForSelectingUsers();
}
Then implement the interface for each supported database:
#Profile("mysql")
#Component
public class MySqlDbQueries implements DbQueries {
#Override
public String createQueryForSelectingUsers() {
return "SELECT * FROM USER";
}
}
2nd example:
#Profile("oracle")
#Component
public class OracleDbQueries implements DbQueries {
#Override
public String createQueryForSelectingUsers() {
return "SELECT * FROM USER";
}
}
Afterwards use it where you need it:
public class MyRepository {
private DbQueries dbQueries;
// DbQueries implementation will be injected based on your current profile
public MyRepository(DbQueries dbQueries) {
this.dbQueries = dbQueries;
}
public void printAllUsers() {
String query = dbQueries.createQueryForSelectingUsers();
// stuff to execute query
}
}
Remember to start your app with a profile with e.q. --spring.profiles.active=mysql or adding active profile information into application.properties:
spring.profiles.active=mysql

Im geting the error Parameter 0 of constructor required a single bean, but 2 were found but one of the beans "found" has been deleted by me

Im trying to set up a h2 database using jpa/jdbc, after creating an implemntation for a query interface using jpa as opposed to jdbc i am now getting the error:
Parameter 0 of constructor in com.nsa.charitystarter.service.CharityQueries required a single bean, but 2 were found:
- charityRepoJDBC: defined in file [C:\Users\V La Roche\Desktop\assessment-1-starter\out\production\classes\com\nsa\charitystarter\data\CharityRepoJDBC.class]
- charityRepoJPA: defined in null
Im unsure as to what has gone wrong and am not really sure where to go from here, i havent been able to find many people with a similar issue to me online.
My implementation using jdbc
#Repository
public class CharityRepoJDBC implements CharityRepository {
private JdbcTemplate jdbc;
private RowMapper<Charity> charityMapper;
#Autowired
public CharityRepoJDBC(JdbcTemplate aTemplate) {
jdbc = aTemplate;
charityMapper = (rs, i) -> new Charity(
rs.getLong("id"),
rs.getString("name"),
rs.getString("registration_id"),
rs.getString("acronym"),
rs.getString("purpose")
);
}
#Override
public List<Charity> findCharityBySearch(String searchTerm) {
String likeSearch = "%" + searchTerm + "%";
return jdbc.query(
"select id, acronym, name, purpose, logo_file_name, registration_id " +
"from charity " +
"where concat(name, acronym, purpose, registration_id) like ?",
new Object[]{likeSearch},
charityMapper);
}
#Override
public Optional<Charity> findById(Long id) {
return Optional.of(
jdbc.queryForObject(
"select id, acronym, name, purpose, logo_file_name, registration_id from charity where id=?",
new Object[]{id},
charityMapper)
);
}
}
Charity finder implementation:
#Service
public class CharityQueries implements CharityFinder {
private CharityRepository charityRepository;
public CharityQueries(CharityRepository repo) {
charityRepository = repo;
}
public Optional<Charity> findCharityByIndex(Integer index) {
return charityRepository.findById(index.longValue());
}
public List<Charity> findCharityBySearch(String searchTerm) {
return charityRepository.findCharityBySearch(searchTerm);
}
}
CharityFinder interface
public interface CharityFinder {
public Optional<Charity> findCharityByIndex(Integer index);
public List<Charity> findCharityBySearch(String searchTerm);
}
error log :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.nsa.charitystarter.service.CharityQueries required a single bean, but 2 were found:
- charityRepoJDBC: defined in file [C:\Users\V La Roche\Desktop\assessment-1-starter\out\production\classes\com\nsa\charitystarter\data\CharityRepoJDBC.class]
- charityRepoJPA: defined in null
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Process finished with exit code 0
You have following definition currently,
#Repository
public class CharityRepoJDBC implements CharityRepository {
And you are injecting CharityRepository in your service layer CharityQueries
#Service
public class CharityQueries implements CharityFinder {
private CharityRepository charityRepository;
Hence when you deploy your application the container is confused which bean you are trying to autowire into the service.
By default spring autowires by type and hence by that there are 2 beans which are qualified to be injected by spring container.
CharityRepository itself and other
CharityRepoJDBC
So you need to either explicitly tell container which bean you are trying to autowire in this case.
So you can try adding qualifiers as below to solve the issue.
#Service
public class CharityQueries implements CharityFinder {
#Qualifier("CharityRepoJDBC")
private CharityRepository charityRepository;
and at the same time modify your CharityRepoJDBC to be,
#Repository(value = "CharityRepoJDBC")
public class CharityRepoJDBC implements CharityRepository {
You seem to have the Spring Data JDBC starter on the classpath and the Spring Data JPA starter.
Spring Data JDBC has a bug which causes it to produce implementation for repository interfaces even if it shouldn't, thus you end up with one implementation from JPA and another one from JDBC.
If you really want to use Spring Data JDBC and Spring Data JPA you can limit the #EnableJdbcRepositories and #EnableJpaRepositories annotations using the include and exclude filters.
But from your code and the tags you used I suspect you might be not at all interested in Spring Data Jdbc but only in Spring Jdbc.
If this is the case look for a dependency spring-boot-starter-data-jdbc and change it to spring-boot-starter-jdbc.
In case all this Spring (Data) JDBC/JPA confuse you this question and its answers might help: Spring Data JDBC / Spring Data JPA vs Hibernate
I solved it putting #Primary annotation in the repository interface.
In your case it would be the following:
#Primary
public interface CharityFinder {
public Optional<Charity> findCharityByIndex(Integer index);
public List<Charity> findCharityBySearch(String searchTerm);
}

How to fix java.lang.IllegalStateException when using spring-data-neo4j

I have a simple test project where checking spring-data-neo4j with spring boot version: 2.1.0.RELEASE (https://github.com/tomkasp/neo4j-playground/blob/master/src/main/java/com/athleticspot/neo4jplayground/domain/AthleteRepository.java)
spring-data-neo4j (version: 5.1.4.RELEASE) dependency is injected by spring-boot-starter-data-neo4j.
My goal was to create a repository method which fetches data with containing and ingnorecase functionalities. In order to do that I've created below method within repository:
public interface AthleteRepository extends CrudRepository<Athlete, Long> {
List<Athlete> findByNameContainingIgnoreCase(String name);
}
When I run above functions I'm getting:
java.lang.IllegalStateException: Unable to ignore case of java.lang.String types, the property 'name' must reference a String
at org.springframework.util.Assert.state(Assert.java:73) ~[spring-core-5.1.2.RELEASE.jar:5.1.2.RELEASE]
at org.springframework.data.neo4j.repository.query.filter.PropertyComparisonBuilder.applyCaseInsensitivityIfShouldIgnoreCase(PropertyComparisonBuilder.java:101) ~[spring-data-neo4j-5.1.2.RELEASE.jar:5.1.2.RELEASE]
Doesn't spring-data-neo4j support Containing and IgnoreCase together? Am I missing something?
At the moment it seems not possible because the referenced org.springframework.data.neo4j.repository.query.filter.PropertyComparisonBuilder seems to allow ignoring case only for "SIMPLE_PROERTY" (is, or equals). See method canIgnoreCase in same class:
private boolean canIgnoreCase(Part part) {
return part.getType() == SIMPLE_PROPERTY && String.class.equals(part.getProperty().getLeafType());
}
Fix is coming with spring 5.2 (Moore): https://jira.spring.io/browse/DATAGRAPH-1190

MappingInstantiationException upon retrieving multi-dim array of doubles from mongo in spring

I am building a spring MVC app with MongoDB. How can I read matrices in spring from mongo? I have a model which persists to mongo just fine using the MongoTemplate class:
Matrix m = new Matrix();
m.setId(UUID.randomUUID().toString());
m.setValues(values);
mongoTemplate.insert(m, "matrix");
The above code works just fine. Values is a double[][] and it is persisted. I am using an extension of the MongoRepository class to make a findAll() call for a list of matrices.
public interface MatrixRepository extends MongoRepository<Matrix, String> {
Matrix findById(String id);
}
And in my service class:
public List<Matrix> readAll() {
return matrixRepository.findAll();
}
This calling this causes the following stack trace:
org.springframework.data.mapping.model.MappingInstantiationException: Could not instantiate bean class [java.lang.Double]: No default constructor found; nested exception is java.lang.NoSuchMethodException: java.lang.Double.<init>()
org.springframework.data.mapping.model.BeanWrapper.<init>(BeanWrapper.java:105)
org.springframework.data.mapping.model.BeanWrapper.create(BeanWrapper.java:73)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:239)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:187)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.readCollectionOrArray(MappingMongoConverter.java:736)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.getValueInternal(MappingMongoConverter.java:695)
org.springframework.data.mongodb.core.convert.MappingMongoConverter$2.doWithPersistentProperty(MappingMongoConverter.java:252)
org.springframework.data.mongodb.core.convert.MappingMongoConverter$2.doWithPersistentProperty(MappingMongoConverter.java:242)
org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:173)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:242)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:187)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:151)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:73)
org.springframework.data.mongodb.core.MongoTemplate$ReadDbObjectCallback.doWith(MongoTemplate.java:1693)
org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:1444)
org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1259)
org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1248)
org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:471)
org.springframework.data.mongodb.repository.support.SimpleMongoRepository.findAll(SimpleMongoRepository.java:255)
org.springframework.data.mongodb.repository.support.SimpleMongoRepository.findAll(SimpleMongoRepository.java:192)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
It happens if your monogo entity class has Double or double fields in spring data mongodb 1.0.0.M5 environment.
You can fix this by replacing your spring data mongodb with a newer version 1.3.3 RELEASE in your pom.xml

Resources