Springboot cassandra frozen value not recognised - spring-boot

I have a springboot application which I hooked up with cassandra. I am trying to create a map inside a map but getting the below exception. Seems to me that even though I use #FrozenValue, it is still sending the cal without the keyword frozen
Error
Caused by: org.springframework.data.cassandra.CassandraInvalidQueryException: Query; CQL [CREATE TABLE IF NOT EXISTS assessmentsubmissionentity (studentid text, assessmentid text, values map<text, map<text, text>>, PRIMARY KEY ((studentid, assessmentid)));]; Non-frozen collections are not allowed inside collections: map<text, map<text, text>>; nested exception is com.datastax.driver.core.exceptions.InvalidQueryException: Non-frozen collections are not allowed inside collections: map<text, map<text, text>>
at org.springframework.data.cassandra.core.cql.CassandraExceptionTranslator.translate(CassandraExceptionTranslator.java:139) ~[spring-data-cassandra-2.1.2.RELEASE.jar:2.1.2.RELEASE]
at org.springframework.data.cassandra.core.cql.CassandraAccessor.translate(CassandraAccessor.java:334) ~[spring-data-cassandra-2.1.2.RELEASE.jar:2.1.2.RELEASE]
Caused by: com.datastax.driver.core.exceptions.InvalidQueryException: Non-frozen collections are not allowed inside collections: map<text, map<text, text>>
at com.datastax.driver.core.exceptions.InvalidQueryException.copy(InvalidQueryException.java:49) ~[cassandra-driver-core-3.6.0.jar:na]
Code
#Table
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class AssessmentSubmissionEntity implements Serializable {
#PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED)
private String studentId;
#PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED)
private String assessmentId;
#FrozenValue
#Column
private Map <String, Map<String, String>> values;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AssessmentSubmissionEntity that = (AssessmentSubmissionEntity) o;
return Objects.equals(studentId, that.studentId) &&
Objects.equals(assessmentId, that.assessmentId) &&
Objects.equals(values, that.values);
}
#Override
public int hashCode() {
return Objects.hash(studentId, assessmentId, values);
}
}
Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
<exclusions>
<exclusion>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId>
</dependency>
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-mapping</artifactId>
</dependency>

It's a problem with Spring Data for Cassandra - they don't have mapping for frozen collections. See DATACASS-465 in their JIRA - it was filed more than year ago, but there is no progress on it...
I think that only solution is to use Object Mapper from the DataStax's Java driver, or manage your tables via CQL.

Related

InvalidSyntaxException passing GraphQL query parameters

Trying to implements the code to be able to execute the following graphql query:
query FIND_BY_BATCHID_LIMIT {
findByBatchIdAndLimit(batch_id: 10, limit: 5) {
id
first_name
last_name
}
}
So the following implementation:
query.graphqls
type Query {
findByBatchIdAndLimit(batch_id: Int!, limit: Int!): [RawEntity]
}
Repository and Query
#Repository
public interface RawRepository extends JpaRepository<RawEntity, Integer> {
#Query(value = "SELECT * FROM rawdata where batch_id = :batch_id LIMIT :limit", nativeQuery = true)
List<RawEntity> findByBatchIdAndLimit(Integer batch_id, Integer limit);
}
#Component
public class Query implements GraphQLQueryResolver {
#Autowired
private RawRepository repository;
public List<RawEntity> findByBatchIdAndLimit(Integer batch_id, Integer limit) {
return repository.findByBatchIdAndLimit(batch_id, limit);
}
}
But getting a:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [graphql.kickstart.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is graphql.parser.InvalidSyntaxException: Invalid Syntax : offending token '!' at line 46 column 11
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
... 139 common frames omitted
Caused by: graphql.parser.InvalidSyntaxException: Invalid Syntax : offending token '!' at line 46 column 11
While the following finder implementation work:
query.graphqls
type Query {
findByLimit(limit: Int!): [RawEntity]
}
Repository and Query
#Repository
public interface RawRepository extends JpaRepository<RawEntity, Integer> {
#Query(value = "SELECT * FROM rawdata LIMIT :limit", nativeQuery = true)
List<RawEntity> findByLimit(Integer limit);
}
#Component
public class Query implements GraphQLQueryResolver {
#Autowired
private RawRepository repository;
public List<RawEntity> findByLimit(Integer limit) {
return repository.findByLimit(limit);
}
}
query FIND_BY_LIMIT {
findByLimit(limit: 5) {
id
first_name
last_name
}
}
So might be the way I'm passing the batch_id, limit params, but googling, sounds the way to pass params.
Any ideas ?
Solved. The issue came from the Voyager dependency:
pom.xml when the question was raised:
<!-- GraphQL -->
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>${graphql.version}</version>
</dependency>
<!-- Voyager-->
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>voyager-spring-boot-autoconfigure</artifactId>
<version>${voyager.version}</version>
<scope>runtime</scope>
</dependency>
Working pom:
<!-- GraphQL -->
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>${graphql.version}</version>
</dependency>

How to map user data type in cassandra using spring boot data

I am looking out for some help related with spring boot data using Cassandra database.
I have following dependencies in my pom.xml.
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId>
<version>2.1.10.3</version>
</dependency>
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-mapping</artifactId>
<version>2.1.10.3</version>
</dependency>
The table structure looks like:
#Table(value = "TEST_ORDERS")
public class OrderDTO {
#PrimaryKey
#Column(value = "email")
private String emailId;
#Column(value = "order_id")
private int orderId;
#Column(value = "creation_date")
private Timestamp creationDate;
#Column(value = "discount_total")
private double discountTotal;
#Column(name = "shipments")
//This is a UDT type
private Set<ShippingDetails> shipments;
//getters and setters here
}
The ShippingDetails object is a UDT with following declartion and i defined as a frozen collection in the cassandra CQL sripts
#UDT(name = "shipping", keyspace = "mc_checkout")
public class ShippingDetails {
#Field(name = "name")
private FullName name;
#Field(name = "quantity_shipped")
private int quantityShipped;
#Field(name = "shipping_address")
private CheckoutAddress shippingAddress;
//so on
}
There is a repository created for the basic CRUD operations:
#Repository
public interface OrderRepository extends CrudRepository<OrderDTO, String> {
}
When i try to invoke findOne API of this repository in my Service class
i get below error:
Cassandra entities must have the #Table, #Persistent or #PrimaryKeyClass Annotation
Spring Data for Apache Cassandra and Datastax' Mapping are two independent tools that try to accomplish the same. Please use either one but don't mix these.
CrudRepository is a Spring Data type while #Field and #UDT are coming from Datastax Mapping.
UDT support for Spring Data for Apache Cassandra is available as of version 1.5, see reference docs. Spring Data for Apache Cassandra 1.5 requires Datastax Java Driver 3.0 or newer.

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.

JPA Stored procedure NoSuchMethodError

I'm following this JPA 2.1 StoredProcedureQuery as an example for what I'm working on.
Anyway, I have a Stored Procedure in the database named "XYZ" and I created a class:
#NameStoredProcedureQuery(
name = "createXYZ",
procedureName = "ZYX",
resultClasses = XYZObj.class,
parameters={
#StoredProcedureParameter(mode=ParameterMode.IN, name = "name", type = String.class),
#StoredProcedureParameter(mode=ParameterMode.OUT, name = "id", type = String.class)})
public class XYZObj {
private String id;
private String name;
...
}
Here is my DAO class:
#Stateless
public class XYZDAO{
#PersistenceContext(unitName = "PU")
private EntityManager em;
public String createXYZ(String name){
StoredProcedureQuery query = em.createNamedStoredProcedureQuery("createXYZ");
query.setParameter("name", name);
query.execute();
return query.getOutputParameterValue("id")
}
...
}
RESTFUL:
#Stateless
#Path("/XYZ")
public class XYZServlet{
#EJB
private XYZDAO xyzDao;
#GET
#Path("test")
public Response test(){
xyzDao.createXYZ("myname");
return Response.satus(Status.NO_CONTENT).build();
}
}
pom.xml
...
<dependencies>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>2.6.0-M3</version>
</dependency>
...
I'm getting NoSuchMethodError at this line.
StoredProcedureQuery query = em.createNamedStoredProcedureQuery("createXYZ");
Does anyone knows why? Am I using the right version of JPA? Did I miss something?
Here is the actual error that i'm getting.
java.lang.ClassCastException: java.lang.NoSuchMethodError cannot be cast to java.lang.exception
at javax.ejb.EJBException.getCauseByException(EJBException.java:23)
at com.sun.jersey.server.impl.ejb.EJBExceptionMapper.toResponse(EJBExceptionMapper.java:63)
at com.sun.jersey.server.impl.ejb.EJBExceptionMapper.toResponse(EJBExceptionMapper.java:54)
at com.sun.jersey.server.impl.ejb.EJBExceptionMapper.toResponse(EJBExceptionMapper.java:67)
at com.sun.jersey.server.impl.ejb.EJBExceptionMapper.toResponse(EJBExceptionMapper.java:54)
truncated. see log file for complete stacktrace
Thanks
Your project compilation looks like it's correctly referencing JPA 2.1, but a NoSuchMethodError indicates that the EntityManager available at runtime is from an earlier JPA version. The EntityManager is probably provided by your EJB container. Consult the documentation of whatever application server you are using to determine how to upgrade to JPA 2.1.
EDIT:
You should also mark whatever maven dependencies that are provided by your application server as <scope>provided</scope> in your pom.xml.

#JsonIgnore is ignored in JUnit-Controller-Unit Test with Mockito

i have constructed my first Spring-Controller Unit Tests and one Controller gives back one object in JSON-Format. The Relations are annotated with #JsonIgnore and it works in my software.
When I simply wanna test those Controllers I'll always get an infinite-Recursion Error on exactly that attribute. It's a 1:1 Relation. On other relations the #JsonIgnore works without any problems.
Tests in error:
save(package.a.b.c.test.ControllerATest): Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError) (through reference chain: package.a.b.c.entity.A["b"]->package.a.b.c.B["a"] etc...
It seems, like my #JsonIgnore is ignored in test-scope?
It's a Maven-managed Project, so i include like this
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
Maybe, somebody made the same experience?
Is the annotation on the fields or their getters? Jackson only looks at the getters by default so moving your annotation to the getters will solve the problem. For example:
class Person {
#JsonIgnore
Set<Department> departments;
public Set<Department> getDepartments() { return this.departments; }
}
class Department {
#JsonIgnore
Set<Person> persons;
public Set<Person> getPersons() return this.persons; }
}
will cause an infinite recursion if a Person and a Department object are referencing each other.
The following will work:
class Person {
Set<Department> departments;
#JsonIgnore
public Set<Department> getDepartments() { return this.departments; }
}
class Department {
Set<Person> persons;
#JsonIgnore
public Set<Person> getPersons() return this.persons; }
}
i just found out, it has to do with bi-directional 1:1 relationships in objects. Switching to newer Version 2.3.1, provided with the following dependencies, solved that issue:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.1</version>
</dependency>

Resources