Cannot access h2 user defined function in select clause but working in where clause - spring-boot

I have created a separate testing project to test this functionality with simple User entity having id and name attributes.
Schema.sql File
Create Table user (
id Int Not Null Primary Key Auto_Increment,
name Varchar(50) Default Null
);
Create Alias TEST FOR
"com.example.demospringbootproject.h2.CustomH2.customTestFunction";
Create Alias ANOTHERTEST FOR
"com.example.demospringbootproject.h2.CustomH2.customAnotherTestFunction";
CustomH2 Function class
package com.example.demospringbootproject.h2;
import com.example.demospringbootproject.model.User;
public class CustomH2 {
public static Integer customTestFunction(Integer id){
return id;
}
public static User customAnotherTestFunction(User user){
return user;
}
}
UserRepository
#Query("Select u From User u where TEST(u.id) = 1")
User repoFunction1();
#Query("Select ANOTHERTEST(u) From User u where u.id = 1")
User repoFunction2();
The first repo function is working fine
The second repo function is generating this error:
java.lang.IllegalArgumentException: Validation failed for query for method public
abstract com.example.demospringbootproject.model.User
com.example.demospringbootproject.repository.UserRepository.repoFunction2()!
I have tried renaming schema.sql to import.sql but same result.
My spring boot version: 2.5.7.
Java version: 1.8
H2 Dependency
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>

Related

nested exception is org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "PERSON" already exists; SQL statement:

I've tried to create JPA small demo project, I faced this problem
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'personJdbcDao': Unsatisfied dependency expressed through field 'jdbcTemplate'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #1 of URL [file:/D:/Learning/c5/springframework/target/classes/schema.sql]: create table person ( ID integer not null, PERSON_NAME varchar(255) not null, LOCATION varchar(255), BIRTH_DATE timestamp, primary key(ID) ); nested exception is org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "PERSON" already exists; SQL statement:
create table person ( ID integer not null, PERSON_NAME varchar(255) not null, LOCATION varchar(255), BIRTH_DATE timestamp, primary key(ID) ) [42101-214]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:660) ~[spring-beans-5.3.24.jar:5.3.24]
Below my config
I made schema.sql for my H2 in the memory database.
src/main/resources/schema.sql
create table person
(
ID integer not null,
PERSON_NAME varchar(255) not null,
LOCATION varchar(255),
BIRTH_DATE timestamp,
primary key(ID)
);
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(1, 'Fazal', 'Peshawar',NOW());
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(2, 'Haroon', 'Islamabad',NOW());
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(3, 'Khan', 'Lahore',NOW());
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(4, 'Khan', 'Islamabad',NOW());
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
Main class:
#SpringBootApplication
public class DemoJPAApplication implements CommandLineRunner {
private Logger logger = LoggerFactory.getLogger(DemoJPAApplication.class);
#Autowired
PersonRepository personRepository;
public static void main(String[] args) {
SpringApplication.run(DemoJPAApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
logger.info("User id 1 -> {}", personRepository.findById(1));
}
}
Repository Class
#Repository
#Transactional
public class PersonRepository {
#PersistenceContext
EntityManager entityManager;
public Person findById(int id){
return entityManager.find(Person.class, id);
}
}
enitity class, here i use lombok
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#ToString
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue
private Integer id;
#Column(name = "person_name")
private String personName;
#Column(name = "location")
private String location;
#Column(name = "birth_date")
private Date birthDate;
}
Could someone find what's wrong with my project? Really appreciate your help.
I run the problem and faced this problem
There is a small error that i face when i run this program.
The error said "Table 'PERSON' already exists" because we are already creating in schema.sql.
Spring boot auto configuration knows that we are using in-memory database H2 and that JPA is in the classpath. It also knows that I'm defining entities as well because i put #Entity in the Person class. So what it does is triggers schema update which is the hibernating feature, it automatically creates this schema for us.
so from now on, i don't need to define CREATE TABLE in schema.sql
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(1, 'Fazal', 'Peshawar',NOW());
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(2, 'Haroon', 'Islamabad',NOW());
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(3, 'Khan', 'Lahore',NOW());
INSERT INTO PERSON (ID, PERSON_NAME, LOCATION, BIRTH_DATE )
VALUES(4, 'Khan', 'Islamabad',NOW());
i remove the CREATE TABLE PERSON from src/main/resources/schema.sql because the table would be created for me by schema update.
Schema update is triggered by a spring boot auto configuration and is one of the hibernate features.

Spring Boot 2.7.1 with Data R2DBC findById() fails when using H2 with MSSQLSERVER compatibility (bad grammar LIMIT 2)

I'm upgrading a Spring Boot 2.6.9 application to the 2.7.x line (2.7.1). The application tests use H2 with MS SQL Server compatibility mode.
I've created a simple sample project to reproduce this issue: https://github.com/codependent/boot-h2
Branches:
main: Spring Boot 2.7.1 - Tests KO
boot26: Spring Boot 2.6.9 - Tests OK
To check the behaviour just run ./mvnw clean test
These are the relevant parts of the code:
Test application.yml
spring:
r2dbc:
url: r2dbc:h2:mem:///testdb?options=MODE=MSSQLServer;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
schema.sql
CREATE SCHEMA IF NOT EXISTS [dbo];
CREATE TABLE IF NOT EXISTS [dbo].[CUSTOMER] (
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
name VARCHAR(255) NOT NULL,
CONSTRAINT PK__otp__D444C58FB26C6D28 PRIMARY KEY (id)
);
Entity
#Table("[dbo].[CUSTOMER]")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class CustomerEntity {
#Id
#Column("id")
private Integer id;
#Column("name")
private String name;
}
Data R2DBC Repository
public interface CustomerRepository extends ReactiveCrudRepository<CustomerEntity, Integer> {
}
The problem occurs when invoking customerRepository.findById(xxx), as can be seen in the following test
#SpringBootTest
#RequiredArgsConstructor
#TestConstructor(autowireMode = ALL)
class BootH2ApplicationTests {
private final CustomerRepository customerRepository;
#Test
void shouldSaveAndLoadUsers() {
CustomerEntity joe = customerRepository.save(new CustomerEntity(null, "Joe")).block();
customerRepository.findById(joe.getId()).block();
Exception:
Caused by: io.r2dbc.spi.R2dbcBadGrammarException:
Syntax error in SQL statement "SELECT [dbo].[CUSTOMER].* FROM [dbo].[CUSTOMER] WHERE [dbo].[CUSTOMER].id = $1 [*]LIMIT 2"; SQL statement:
SELECT [dbo].[CUSTOMER].* FROM [dbo].[CUSTOMER] WHERE [dbo].[CUSTOMER].id = $1 LIMIT 2 [42000-214]
The R2dbcEntityTemplate is limiting the selectOne query to 2 elements:
public <T> Mono<T> selectOne(Query query, Class<T> entityClass) throws DataAccessException {
return (Mono)this.doSelect(query.getLimit() != -1 ? query : query.limit(2), entityClass, this.getTableName(entityClass), entityClass, RowsFetchSpec::one);
}
And this is translated into a LIMIT N clause which is not supported by H2/SQL Server.
Not sure if it's some kind of H2/Spring Data bug or there's a way to fix this.
It will be solved in Spring Data 2.4.3 (2021.2.3)

Ignite CrudRepository still getting name conflict for deleteAll

I using ignite core, and ignite-spring-data both 2.7.0
compile "org.apache.ignite:ignite-core:2.7.0"
compile "org.apache.ignite:ignite-spring-data:2.7.0"
compile('org.springframework.boot:spring-boot-starter-jdbc:2.1.5.RELEASE');
But I still get this error:
error: name clash: deleteAll(Iterable<? extends T>) in CrudRepository and deleteAll(Iterable<ID>) in IgniteRepository have the same erasure, yet neither overrides the other
where T,ID are type-variables:
T extends Object declared in interface CrudRepository
ID extends Serializable declared in interface IgniteRepository
According to https://issues.apache.org/jira/browse/IGNITE-6879 this issue was resolved with version 2.7.0 so why am I still getting it?
if I use instead:
compile "org.apache.ignite:ignite-spring-data_2.0:2.7.0"
It seems to break everything, so I am not sure that an option.
import org.apache.ignite.springdata.repository.IgniteRepository;
import org.apache.ignite.springdata.repository.config.Query;
import org.apache.ignite.springdata.repository.config.RepositoryConfig;
#RepositoryConfig(cacheName = "PersonCache")
public interface PersonRepository extends IgniteRepository<Person, Long> {
List<Person> findByFirstNameAndLastName(String firstName, String lastName);
#Query("SELECT c.* FROM Person p JOIN \"ContactCache\".Contact c ON p.id=c.personId WHERE p.firstName=? and p.lastName=?")
List<Contact> selectContacts(String firstName, String lastName);
#Query("SELECT p.id, p.firstName, p.lastName, c.id, c.type, c.location FROM Person p JOIN \"ContactCache\".Contact c ON p.id=c.personId WHERE p.firstName=? and p.lastName=?")
List<List<?>> selectContacts2(String firstName, String lastName);
}
Just switch to new 2.0 support version of ignite spring data.
compile "org.apache.ignite:ignite-spring-data_2.0:${igniteVersion}"
import org.apache.ignite.springdata20.repository.IgniteRepository;
import org.apache.ignite.springdata20.repository.config.Query;
import org.apache.ignite.springdata20.repository.config.RepositoryConfig;
No worries!
After changing to use:
<dependency>
<groupId>org.apache.ignite</groupId>
<artifactId>ignite-spring-data_2.0</artifactId>
<version>${ignite.version}</version>
</dependency>
.....
<ignite.version>2.7.0</ignite.version>
You will have to re-import everything. so just delete the 'import' part from the code all together. Since now the IgniteRepository etc are coming from a package called org.apache.ignite.springdata20.repository .... instead of the old one.

How to Query Cassandra using CassandraRepository with Spring Data Cassandra and Spring Boot?

I have a Table in my Cassandra Cluster built using these commands:
CREATE KEYSPACE IF NOT EXISTS activitylogs WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'} AND durable_writes = true;
CREATE TABLE IF NOT EXISTS activitylogs.activities2 (
activity_id timeuuid,
actor_id text,
app_id text,
item_id text,
viewer_id text,
activity_type int,
ts timestamp,
PRIMARY KEY (actor_id, activity_id, app_id)
) WITH CLUSTERING ORDER BY (activity_id DESC, app_id ASC);
This is what my Repository in Spring Project looks like:
public interface ActivityRepository extends CassandraRepository<Activity> {
#Query("SELECT actor_id FROM activities2 WHERE actor_id='actorA'")
Iterable<Activity> findByActor_Id(String actor_Id);
}
Now, when I access the endpoint associated with this query retriever , I get the following error:
Invalid null value in condition for column actor_id
at com.datastax.driver.core.Responses$Error.asException(Responses.java:136)
However, when I run the equivalent CQL command in CQLSHell I get the exact row I was looking for...
Why is this happening ?
There was a mistake in writing the controller for the SpringBoot app.
The proper controller is thus:
#RequestMapping(value = "/activity/{actor_id}",method = RequestMethod.GET)
#ResponseBody
public List<Activity> activity1(**#PathVariable("actor_id")** String actor_id) {
List<Activity> activities = new ArrayList<>();
activityRepository.findByActor_Id(actor_id).forEach(e->activities.add(e));
return activities;
}
Earlier implementation (at the time of writing the question) did not have #PathVariable annotation hence, controller wasn't passing any value to the repository interface function.

How should I create H2 using SpringBoot?

I'm starting to play with Spring Boot and as part of that I want to create an in memory DB to work with and bootstrap with the application.
Given the config/code below I get no errors in the startup log and can access the application ok, so it does startup (I get template errors about objects not existing), but I don't get any data back from the DAO when calling findAll() (or if I try to call findById(int) ).
So while it seems things are ok (no error in log, log shows it finds the sql to create schema ad attempts to run the data.sql statements) when I try to access data via the DAO I get no Exception, but no data returned.
Any ideas or observations on the code that might be a problem?
I've added the Spring Data / H2 stuff to my pom:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
Spring DAO:
public interface PersonDao extends CrudRepository<Person, Integer> {
}
DB props in application.properties:
server.contextPath=/
server.port=8080
spring.mvc.view.suffix=.ftl
datasource.mine.jdbcUrl=jdbc:h2:tcp://localhost/mem:clubmanagement
datasource.mine.user=sa
datasource.mine.password=
datasource.mine.poolSize=30
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=DEBUG
spring.jpa.hibernate.ddl-auto=create
My service:
#Service
public class MemberServiceImpl implements MemberService {
#Autowired
PersonDao dao;
#Override
public Optional<ClubMember> getClubMember(int id) {
Person dbPerson = dao.findOne(id);
if(dbPerson == null) {
return Optional.empty();
}
return Optional.of(fromEntity(dbPerson));
}
#Override
public List<ClubMember> allMembers() {
Iterable<Person> people = dao.findAll();
List<ClubMember> members = new ArrayList<>();
people.forEach(person -> {
members.add(fromEntity(person));
});
return members;
}
private ClubMember fromEntity(Person p) {
ClubMember member = new ClubMember();
member.setCurrentGrade(p.getCurrentGrade());
member.setFirstName(p.getFirstName());
member.setLastName(p.getLastName());
member.setAssociationMemberId(p.getAssociationMemberId());
member.setLastGradingDate(p.getLastGradingDate());
return member;
}
}
Schema.sql in resources/ :
create table CLUB
(id int not null, name varchar(60), association_member_id int);
create table PERSON
(
id int not null, grade_id int, first_name varchar(35), last_name varchar(35),
association_membership varchar(12), last_grading_date date
);
create table GRADE
(id int not null, name varchar(20));
In data.sql (again in resources directory):
insert into club (id, name, association_member_id) values (1, 'some club', '123');
insert into person (id, grade_id, first_name, last_name, association_membership, last_grading_date)
values (1, 1, 'name', 'lastname', 'a1234', '2016-03-23');
Entity class I am trying to retrieve (Trying to use Lombock, also new to me, to generate getters/setters):
#Entity
#Table(name = "person")
public #Data class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#JoinColumn(name = "grade_id")
private GRADE currentGrade;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "association_membership")
private String associationMemberId;
#Column(name = "last_grading_date")
#Temporal(value = TemporalType.DATE)
private Date lastGradingDate;
}
you want to add H2 database, but you added HSQLDB, please replace
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
with
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
EDIT
I noticed you have multiple issues in your code:
default schema file name is schema.sql not Schema.sql
names of tables in schema.sql are different than names in data.sql (PERSON vs person)
you used this spring.jpa.hibernate.ddl-auto=create in application.properties (default option), in this case JPA databases schema only will be automatically created (without data creation), so data.sql will not be executed, to fix this issues you can use validate or update option
I will write one simple example how to use H2 database with spring boot and JPA
This is the project structure:
Grade Entity
package com.shijazi;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="GRADE")
public class Grade {
#Id
#GeneratedValue
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Grade(int id, String name) {
super();
this.id = id;
this.name = name;
}
public Grade() {
}
}
GradeRepository.java
package com.shijazi;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface GradeRepository extends JpaRepository<Grade, Integer> {
}
Application.java
#SpringBootApplication
#RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Autowired
private GradeRepository gradeRepo;
#RequestMapping(value="api/test")
public List<Grade> getall()
{
return gradeRepo.findAll();
}
}
application.properties
spring.jpa.hibernate.ddl-auto=validate
schema.sql
create table GRADE (id int not null, name varchar(20));
data.sql
insert into GRADE (id, name) values (2, 'name');
Dependencies in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
Now just run application and call this URL: http://localhost:8080/api/test
try to change the spring.jpa.hibernate.ddl-auto and see results
if you activate ddl-auto and have a schema.sql, BOTH of them are executed. But normally schema.sql is executed first. So the ddl-auto throws everything away, which was created by schema.sql and data.sql
After spending some time working through some ideas with #Safwan Hijazi in chat, came to the conclusion that what is happening is that the schema.sql and data.sql were being run but then the schema was recreated depending on the value (or lack of) of the spring.jpa.hibernate.ddl-auto property.
If not specified, spring/hibernate between them ended up recreating an empty schema (default seems to be create-drop for in memory DB).
If set the 'none' then that wouldn't happen and DB as created by the schema and data sql scripts would remain and the application functioned correctly.
See also: CrudRepository not reading data from schema.sql
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Just gives you an spring boot opinionated inclusion to the spring-boot-starter-data-jpa maven file, for the bill of material of all the dependencies. To use any one of the dependency defined in the Dependency management of the spring-boot-starter-data-jpa pom you will have to explicitly declare the dependency in dependency section of your pom file.
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
To get the h2 database up and running with your application, you can specifiy the properties in your application.properties file at src/main/resources/application.properties use:
spring.h2.console.enabled=true
spring.h2.console.path=/h2DB
so when you run your application with spring application starter, you will be able to access the application at http://localhost:8080/h2DB login to the DB and you can verify if the database had the inserts in it or not?
Don't find the data in there then you know where to make a change to keep the data there.

Resources