Spring Cassandra store a List with custom object's - java

I like to store a object like:
#Table(value = "my_table")
public class MyTableDto {
#PrimaryKeyColumn(name = "uid", type = PrimaryKeyType.PARTITIONED)
#CassandraType(type = DataType.Name.UUID)
private UUID uid;
#Column(value = "child_ids")
private List<ChildIdDto> childIds;
}
Then I get the exception:
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Only primitive types are allowed inside Collections for property [childIds] of type ['interface java.util.List'] in entity [de.myapplication.repository.dto.MyTableDto]
I do understand the exception, but is there another way to persist custom objects?
EDIT:
When I comment out this attribute, everything works

! Never say never, I got the solution.
To give a good example, I will list all according classes.
ParentClass.java
#Table(value = "my_table") //OPT
public class MyTableDto {
#PrimaryKeyColumn(name = "uid", type = PrimaryKeyType.PARTITIONED)
#CassandraType(type = DataType.Name.UUID)
private UUID uid;
#Column(value = "child_ids") //OPT
private List<ChildDto> childIds;
}
ChildDto.java
#UserDefinedType // THE SOLUTION
public class ChildDto {
#Column(value = "child") //OPT
#CassandraType(type = DataType.Name.TEXT) //OPT
private String groupId;
#Column(value = "description") //OPT
#CassandraType(type = Name.TEXT) //OPT
private String description;
}
The #UserDefinedType is the solution.
For more information see here.
NOTE: Each annotation with "OPT" is NOT required

Related

Serialization with Jackson XmlMapper

I'm trying to serialize object to xml string with Jackson XmlMapper. My object is:
#JacksonXmlRootElement(namespace = "http://www.w3.org/2001/XMLSchema", localName = "PersonRO")
public class PersonInfo {
#JacksonXmlProperty(localName = "PersonID")
private String personId;
#JacksonXmlProperty(localName = "ReturnCode")
private Integer errorCode;
// getters, setters
}
I need to achieve following xml in output:
<PersonRO xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<PersonID>00000000000001</PersonID>
<ReturnCode>150</ReturnCode>
</PersonRO>
The task seems easy, but first of all I have a problem with achieving multiple namespases (xmlns:xsd, xmlns:xsi), and also have empty namespaces for fields, although I don't need them at all.
So far my result is:
<PersonRO xmlns="http://www.w3.org/2001/XMLSchema">
<PersonID xmlns="">00000000000001</PersonID>
<ReturnCode xmlns="">150</ReturnCode>
</PersonRO>
So, how can I achieve exactly the same result as above, using Jackson XmlMapper?
(I've seen that you can configure XmlFactory, etc., but can not do it properly...)
If you need any clarification, please let me know and thank you in advance.
I have found the answer:
#JacksonXmlRootElement(localName = "PersonRO")
public class PersonInfo {
#JacksonXmlProperty(isAttribute = true, localName = "xmlns:xsd")
private final String xmlnsXsd = "http://www.w3.org/2001/XMLSchema";
#JacksonXmlProperty(isAttribute = true, localName = "xmlns:xsi")
private final String xmlnsXsi = "http://www.w3.org/2001/XMLSchema-instance";
#JacksonXmlProperty(localName = "PersonID")
private String personId;
#JacksonXmlProperty(localName = "ReturnCode")
private Integer errorCode;
// getters, setters
}

JsonPropertyOrder not ordering correctly

so as you can see from the title my #JsonPropertyOrder is not ordering how I want... this is my class(see code bellow) and everything is ordered good except the zpp attribute, it goes between "spravce" and "ziskatele". I tried to rename it reorder it and its totally ignored.Thank you for all the answers :)
(JacksonXML ver 2.9.8)
#JacksonXmlRootElement(localName = "xmlroot")
#JsonPropertyOrder({"cnt-unik-id","kod-produktu","frekvence","datum-sjednani",
"pocatek","konec","spravce","ziskatele","objekty-unik-id","udaje","objekty-all","adresy","zpp"})
public class ContractDetail{
#JacksonXmlProperty(localName = "zpp")
private Integer zpplID;
#JacksonXmlProperty(localName = "cnt-unik-id")
private Integer id;
#JacksonXmlProperty(localName = "kod-produktu")
private Item product;
#JacksonXmlProperty(localName = "spravce")
private Item administrator;
#JacksonXmlElementWrapper(localName = "ziskatele")
#JacksonXmlProperty(localName = "xml-ziskatel")
private List<Customer> customers;
#JacksonXmlProperty(localName = "frekvence")
private Item frequency;
#JacksonXmlProperty(localName = "datum-sjednani")
private Item createdAt;
#JacksonXmlProperty(localName = "pocatek")
private Item startDate;
#JacksonXmlProperty(localName = "konec")
private Item endDate;
#JacksonXmlElementWrapper(localName = "objekty-unik-id")
#JacksonXmlProperty(localName = "int")
private List<Integer> vehicle;
#JacksonXmlProperty(localName = "xml-hodnota")
#JacksonXmlElementWrapper(localName = "udaje")
private List<Item> values;
#JacksonXmlProperty(localName = "xml-objekt")
#JacksonXmlElementWrapper(localName = "objekty-all")
private List<ObjectItem> objects;
#JacksonXmlElementWrapper(localName = "adresy")
#JacksonXmlProperty(localName = "xml-adresa")
private List<AddressItem> address;
//getters setters contructors stuff
}
Use the Java field names, instead of the XML element names.
For example, using a simplified version of your ContractDetail class:
Using this:
#JsonPropertyOrder({"id", "vehicle", "zpplID"})
Generates this:
<xmlroot>
<cnt-unik-id>123</cnt-unik-id>
<objekty-unik-id>
<int>678</int>
<int>789</int>
</objekty-unik-id>
<zpplID>456</zpplID>
</xmlroot>
And using this:
#JsonPropertyOrder({"vehicle", "zpplID", "id"})
Generates this:
<xmlroot>
<objekty-unik-id>
<int>678</int>
<int>789</int>
</objekty-unik-id>
<zpplID>456</zpplID>
<cnt-unik-id>123</cnt-unik-id>
</xmlroot>
Soo #andrewjames's answer works but If someone still needs/wants to use xml element names the solution I came up with looks like this:
#JsonPropertyOrder({"cnt-unik-id","kod-produktu","frekvence","datum-sjednani",
"pocatek","konec","spravce","ziskatele","objekty-unik-id","int","udaje","xml-hodnota","objekty-all","xml-objekt","adresy","xml-adresa","zpp"})

Using complex data structures in spring data for Cassandra

I'm working on setting up a DAO for Cassandra in spring.
Now I have a question regarding using composite classes multiple times in an object.
I have this class Setting:
#Table(value = "settings")
public class Setting {
#PrimaryKey
private User owner;
#Column("key")
private String key;
#Column("value")
private String value;
#Column("updated_by")
private User updatedBy;
}
And the class User:
#PrimaryKeyClass
public class User implements Serializable{
#PrimaryKeyColumn(name = "userId", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
private String id;
#PrimaryKeyColumn(name = "userIdType", ordinal = 1, type = PrimaryKeyType.PARTITIONED)
private String idType;
}
So I'm using the class User twice. Once as primary key "owner" and once as "updatedBy".
When using this in the CassandraOperations classes, it works fine as the primary key, but not again as another column.
It complains about column name userId already being used. Makes sense.
So how can I make this work?
I could use UserDefined Types perhaps?
CREATE TYPE test_keyspace.user (
id text,
id_type text
);
But how can I do that from java Annotations?
Or, how can I reuse the same class otherwise?
Considering the relatively simple data structures in Cassandra, I am ok with flattening the User class as a single String like idType.id too.
Thanks for any help!
Ok, found it here, answered by denzal.
My classes now look like:
#Table("settings")
public class Setting {
#PrimaryKeyColumn(name = "owner", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
#CassandraType(type = DataType.Name.UDT, userTypeName = "user_type")
private User owner;
#PrimaryKeyColumn(name = "key", ordinal = 1, type = PrimaryKeyType.CLUSTERED)
#CassandraType(type = DataType.Name.TEXT)
private String key;
#CassandraType(type = DataType.Name.TEXT)
private String value;
#CassandraType(type = DataType.Name.UDT, userTypeName = "user_type")
private User lastUpdatedBy;
}
And User Class:
#UserDefinedType("user_type")
public class User implements Serializable{
#CassandraType(type = DataType.Name.TEXT)
private String id;
#CassandraType(type = DataType.Name.TEXT)
private IdType idType;
}
Works nicely.

Applying UDT on Spring Data

I have created custom defined types in cassandra and I'm trying to insert the data using spring. At first i thought using entity like this would work:
#PrimaryKeyColumn(name = "appid", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
private String appid;
#PrimaryKeyColumn(name = "event", ordinal = 1, type = PrimaryKeyType.PARTITIONED, ordering = Ordering.ASCENDING)
private String event;
#PrimaryKeyColumn(name = "date", ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING)
private Date date;
#PrimaryKeyColumn(name = "userid", ordinal = 3, type = PrimaryKeyType.PARTITIONED)
private String userid;
#Column (name = "items")
#Frozen
private Map<CassandraEventParamModel,CassandraEventStatsModel> items;
And created a UDT in java like this (param from create type):
#UDT(keyspace = "ms_analytics", name = "param")
public class CassandraEventParamModel {
#Field(name = "key")
public String key;
#Field(name = "value")
public String value;
but I kept getting an error which says
Invocation of init method failed; nested exception is org.springframework.data.cassandra.mapping.VerifierMappingExceptions:
Cassandra entities must have the #Table, #Persistent or #PrimaryKeyClass Annotation
I wonder what is the proper way to deliver data using spring-data into cassandra.
Thanks

Hibernate annotation using #Parameter is not working

I am using Hibernate custom user type to map enum to a table varchar/char. I am using custom user type code from here (https://community.jboss.org/wiki/Java5StringValuedEnumUserType). I am trying following annotation to do the mapping but its not working.
#Transient
#Type(type = "data.model.base.StringValuedEnumType", parameters = {
#Parameter(name = "enumClass", value = "data.common.TypeEnum"),
#Parameter(name = "identifierMethod", value = "dbCode") })
private TypeEnum typeEnum;
TypeEnum code:
public enum TypeEnum implements StringValuedEnum {
OFF("OFF", "O"),
ON("ON, "O"),
private String dbCode;
private String desc;
TypeEnum(String desc, String dbCode) {
this.desc=desc;
this.dbCode = dbCode;
}
#Override
public String dbCode() {
return dbCode;
}
public String desc() {
return desc;
}
}
I believe I am doing something wrong in the annotation but I am not able to figure out what is it. Any idea anyone?
I have found it. I updated the annotation by removing #Transient and adding in a #Column for the mapping. I also updated the code to take care of the passed name and removed defaultValue.
#Column(name = "TYP_CD", length = 1)
#Type(type = "data.model.base.StringValuedEnumType", parameters = {
#Parameter(name = "enumClass", value = "data.common.TypeEnum")})
private TypeEnum typeEnum;

Categories