I have a Java class that is using the datastax cassandra driver to write a pojo to a cassandra table. Everything works fine, until it comes to having to write a class object to the cassandra table. It throws this error:
Caused by: com.datastax.driver.core.exceptions.CodecNotFoundException: Codec not found for requested operation: [frozen< projKeySpace.smi > <-> code.generic.common.data.MyCustomSmiObject]
So I have tried a lot of different things to try and make the attribute "Frozen", but nothing works and I keep getting the same error. Here is an example of the class object.
#Table(keyspace="projkeyspace", name="summarytable")
public class DataGroupingObject implements Serializable {
#Column(name = "objid")
private String objId;
#Column(name = "timeofjob")
private Date timeOfJob;
#Column(name = "smiobjectinput")
#Frozen
//Have also tried:
//#Frozen("frozen<projKeySpace.smi>")
//#Frozen("frozen<smi>")
//#Frozen("frozen<MyCustomSmiObject>")
//And all other permutations I can think of...
private MyCustomSmiObject myCustomSmiObject; //The problem attribute
#Column(name = "column5")
private String dataForColumn5;
//Getters and setters....
}
So what am I overlooking? Digging into the datastax documentation didn't show much beyond this, http://docs.datastax.com/en/drivers/java/2.2/com/datastax/driver/mapping/annotations/Frozen.html , which I tried.
I also have tried having the MyCustomSmiObject be mapped to the frozen 'projkeyspace.smi' and that didn't work (of course I didn't think it would since there isn't actually a table in cassandra called smi, its just a type) but here is an example of it:
#Table(keyspace="projkeyspace", name="smi")
public class MyCustomSmiObject implements Serializable {
#Column(name = "idstring")
private String idString;
#Column(name = "valuenum")
private Double valueNum;
//Getters and Setters....
}
So like I said, I am at a loss. Any help would be greatly appreciated and thanks in advance!
smi is a UDT isn't it? In that case MyCustomSmiObject should be annotated with #UDT(keyspace="projkeyspace", name="smi") instead of #Table. By doing that, the driver should detect that this is a UDT and it will register a custom codec for it which will allow it to be able to properly serialize and deserialize it.
On another note the #Frozen annotation currently has no impact on the mapper, it is only informational at this time until the mapper has support for schema generation.
Related
I have following entity-model which I'll save to my MongoDB:
#Document(collection = "googleplaygames")
#Component
public final class GooglePlayGame implements Serializable {
#Id
private String title;
private String genre;
private String price;
private LocalDate dateOfLastUpdate;
...
This code allows me to save duplicates Game objects. I found annotation #Indexed and rewrote code:
#Document(collection = "googleplaygames")
#Component
public final class GooglePlayGame implements Serializable {
#Indexed(unique=true)
private String title;
private String genre;
private String price;
private LocalDate dateOfLastUpdate;
...
Now if I'll try to save entity with same title, I'll receive org.springframework.dao.DuplicateKeyException. Fair enough.
And I found this "error" in logs while Spring Boot app is starting:
2020-06-26 13:26:52,303 WARN [restartedMain] org.springframework.data.mongodb.core.index.JustOnceLogger: Automatic index creation will be disabled by default as of Spring Data MongoDB 3.x.
Please use 'MongoMappingContext#setAutoIndexCreation(boolean)' or override 'MongoConfigurationSupport#autoIndexCreation()' to be explicit.
However, we recommend setting up indices manually in an application ready block. You may use index derivation there as well.
> -----------------------------------------------------------------------------------------
> #EventListener(ApplicationReadyEvent.class)
> public void initIndicesAfterStartup() {
>
> IndexOperations indexOps = mongoTemplate.indexOps(DomainType.class);
>
> IndexResolver resolver = new MongoPersistentEntityIndexResolver(mongoMappingContext);
> resolver.resolveIndexFor(DomainType.class).forEach(indexOps::ensureIndex);
> }
> -----------------------------------------------------------------------------------------
But I want to update the enity, if any of other fields are different. For example, if I have an entity with title "Dead Cells" and version "1.0" in my db, I want to update this entity if version now is "1.1". But code above doesn't allow me to do this.
So what is this error? And how to update entity (doesnt allow duplicate by title field, but allow to rewrite entity, if other fields were changed).
According to how you put the question, if you are updating a document, you shouldn't receive the DuplicateKeyException, this only occurs if you have another document with the index value duplicated. Check you are applying the save() method of your repository (as I presume you do) on and existent instance of a Mongo document.
If you want to control duplications, you could use a compound index. A compound index includes more than one field in its definition, so it's faster to make searches for including in the criteria the fields of the index. Including the unicity constraint you'll be able to forbid the duplication of such values in another documents.
Let's say, according to your example you want to avoid titles and genre duplication, then you could define an index as:
#Document(collection = "googleplaygames")
#CompoundIndex(def = "{'title':1,'genre':1}", unique = true)
#Component
public final class GooglePlayGame implements Serializable {
private String title;
private String genre;
private String price;
private LocalDate dateOfLastUpdate;
...
With this index, if you have a document:
{"title":"Pacman","genre":"arcade"}
And try to create a new document with the same values for title and genre you will get the DuplicateKeyException.
If you try to create a document:
{"title":"Pacman","genre":"mobile"}
The you will have two documents.
I have made an entity in Jpa as shown below and it is working fine at the end it is representing the table as per the Jpa specifications now in this entity I have kept a member of type ByteBuffer so that at the backend in the corresponding table it is of Blob Data type , now the problem is that functionality is working fine but when i scan the code as per sonar lint for this class it gives me the exception that makes "configuration" transient or serializable .
But the point is that my class already implements serializable so please advise how to overcome this problem, please advise
#Table( value = "abc_configurations" )
public class Abc implements Serializable {
private static final long serialVersionUID = 22528067957L;
#PrimaryKeyColumn( name = "abcCode", type = PARTITIONED )
private String abcCode;
#Column
private ByteBuffer configuration;
// setters and getteres
//default constructor
}
I followed everything that is outlined here - https://github.com/derjust/spring-data-dynamodb/wiki/Use-Hash-Range-keys. But still no luck.
I have a DynamoDB table with a hash key and a sort key.
Here is my entity class RecentlyPlayed.class
#DynamoDBTable(tableName="some-table")
public class RecentlyPlayed {
#Id
private RecentlyPlayedId recentlyPlayedId;
// ----- Constructor methods -----
#DynamoDBHashKey(attributeName="keyA")
// Getter and setter
#DynamoDBRangeKey(attributeName="keyB")
// Getter and setter
}
Here is my key class RecentlyPlayedId.class
public class RecentlyPlayedId implements Serializable {
private static final long serialVersionUID = 1L;
private String keyA;
private String keyB;
public RecentlyPlayedId(String keyA, String keyB) {
this.keyA = keyA;
this.keyB = keyB;
}
#DynamoDBHashKey
// Getter and setter
#DynamoDBRangeKey
// Getter and setter
}
Here is my repository interface RecentlyPlayedRepository
#EnableScan
public interface RecentlyPlayedRepository extends CrudRepository<RecentlyPlayed, RecentlyPlayedId> {
List<RecentlyPlayed> findAllByKeyA(#Param("keyA") String keyA);
// Finding the entry for keyA with highest keyB
RecentlyPlayed findTop1ByKeyAOrderByKeyBDesc(#Param("keyA") String keyA);
}
I am trying to save an object like this
RecentlyPlayed rp = new RecentlyPlayed(...);
dynamoDBMapper.save(rp); // Throws that error
recentlyPlayedRepository.save(rp); // Also throws the same error
I am using Spring v2.0.1.RELEASE. The wiki in the original docs warns about this error and describes what to do to mitigate. I did exactly what they said. But still no luck.
The link to that wiki is here - https://github.com/derjust/spring-data-dynamodb/wiki/Use-Hash-Range-keys
DynamoDB only supports primitive data types, it does not know how to convert your complex field (recentlyPlayedId) into a primitive, such as a String.
To show that this is the case, you can add the annotation #DynamoDBIgnore to your recentlyPlayedId attribute like this:
#DynamoDBIgnore
private RecentlyPlayedId recentlyPlayedId;
You also need to remove the #id annotation.
Your save function will then work, but the recentlyPlayedId will not be stored in the item. If you do want to save this field, you need to use the #DynamoDBTypeConverted annotation and write a converter class. The converter class defines how to convert the complex field into a String, and then uncovert the String into the complex field.
Removing getters/setters for the #Id field fixed the problem for me. This is suggested in https://github.com/derjust/spring-data-dynamodb/wiki/Use-Hash-Range-keys
not supported; requires #DynamoDBTyped or #DynamoDBTypeConverted",
i was getting this error when i defined model class with field JsonNode,i converted it to MAP<String,String>,now it is working fine
First, here are my entities.
Player :
#Entity
#JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class,
property="id")
public class Player {
// other fields
#ManyToOne
#JoinColumn(name = "pla_fk_n_teamId")
private Team team;
// methods
}
Team :
#Entity
#JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class,
property="id")
public class Team {
// other fields
#OneToMany(mappedBy = "team")
private List<Player> members;
// methods
}
As many topics already stated, you can avoid the StackOverflowExeption in your WebService in many ways with Jackson.
That's cool and all but JPA still constructs an entity with infinite recursion to another entity before the serialization. This is just ugly ans the request takes much longer. Check this screenshot : IntelliJ debugger
Is there a way to fix it ? Knowing that I want different results depending on the endpoint. Examples :
endpoint /teams/{id} => Team={id..., members=[Player={id..., team=null}]}
endpoint /members/{id} => Player={id..., team={id..., members=null}}
Thank you!
EDIT : maybe the question isn't very clear giving the answers I get so I'll try to be more precise.
I know that it is possible to prevent the infinite recursion either with Jackson (#JSONIgnore, #JsonManagedReference/#JSONBackReference etc.) or by doing some mapping into DTO. The problem I still see is this : both of the above are post-query processing. The object that Spring JPA returns will still be (for example) a Team, containing a list of players, containing a team, containing a list of players, etc. etc.
I would like to know if there is a way to tell JPA or the repository (or anything) to not bind entities within entities over and over again?
Here is how I handle this problem in my projects.
I used the concept of data transfer objects, implemented in two version: a full object and a light object.
I define a object containing the referenced entities as List as Dto (data transfer object that only holds serializable values) and I define a object without the referenced entities as Info.
A Info object only hold information about the very entity itself and not about relations.
Now when I deliver a Dto object over a REST API, I simply put Info objects for the references.
Let's assume I deliever a PlayerDto over GET /players/1:
public class PlayerDto{
private String playerName;
private String playercountry;
private TeamInfo;
}
Whereas the TeamInfo object looks like
public class TeamInfo {
private String teamName;
private String teamColor;
}
compared to a TeamDto
public class TeamDto{
private String teamName;
private String teamColor;
private List<PlayerInfo> players;
}
This avoids an endless serialization and also makes a logical end for your rest resources as other wise you should be able to GET /player/1/team/player/1/team
Additionally, the concept clearly separates the data layer from the client layer (in this case the REST API), as you don't pass the actually entity object to the interface. For this, you convert the actual entity inside your service layer to a Dto or Info. I use http://modelmapper.org/ for this, as it's super easy (one short method call).
Also I fetch all referenced entities lazily. My service method which gets the entity and converts it to the Dto there for runs inside of a transaction scope, which is good practice anyway.
Lazy fetching
To tell JPA to fetch a entity lazily, simply modify your relationship annotation by defining the fetch type. The default value for this is fetch = FetchType.EAGER which in your situation is problematic. That is why you should change it to fetch = FetchType.LAZY
public class TeamEntity {
#OneToMany(mappedBy = "team",fetch = FetchType.LAZY)
private List<PlayerEntity> members;
}
Likewise the Player
public class PlayerEntity {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "pla_fk_n_teamId")
private TeamEntity team;
}
When calling your repository method from your service layer, it is important, that this is happening within a #Transactional scope, otherwise, you won't be able to get the lazily referenced entity. Which would look like this:
#Transactional(readOnly = true)
public TeamDto getTeamByName(String teamName){
TeamEntity entity= teamRepository.getTeamByName(teamName);
return modelMapper.map(entity,TeamDto.class);
}
In my case I realized I did not need a bidirectional (One To Many-Many To One) relationship.
This fixed my issue:
// Team Class:
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Player> members = new HashSet<Player>();
// Player Class - These three lines removed:
// #ManyToOne
// #JoinColumn(name = "pla_fk_n_teamId")
// private Team team;
Project Lombok might also produce this issue. Try adding #ToString and #EqualsAndHashCode if you are using Lombok.
#Data
#Entity
#EqualsAndHashCode(exclude = { "members"}) // This,
#ToString(exclude = { "members"}) // and this
public class Team implements Serializable {
// ...
This is a nice guide on infinite recursion annotations https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
You can use #JsonIgnoreProperties annotation to avoid infinite loop, like this:
#JsonIgnoreProperties("members")
private Team team;
or like this:
#JsonIgnoreProperties("team")
private List<Player> members;
or both.
We use annotations for mapping the entity class with the database table by simply specifying #Entity and more like #Id, table joins and many things. I do not know how these entity variables are getting mapped with database table. Can anyone give a short description for understanding.
Thanks :)
Well the idea is to translate your objects and their connections with other objects into a relational database. These two ways of representing data (objects defined by classes and in tables in a database) are not directly compatible and that is where a so called Object Relational Mapper framework comes into play.
So a class like
class MyObject
{
private String name;
private int age;
private String password;
// Getters and setters
}
Will translate into a database table containing a column name which is of type varchar, age of type int and password of type varchar.
Annotations in Java simply add additional information (so called meta data) to your class definitions, which can be read by any other class (e.g. JavaDoc) and in the case of the Java Persistence API will be used by an ORM framework like Hibernate to read additional information you need to translate your object into the database (your database table needs a primary id and some information - like what type of a relation an object has to another - can't be automatically determined by just looking at your class definition).
Annotations are very well explained here:
http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/
annotations are just metadata on a class, nothing magical. You can write your own annotations. Those annotations are given retention policies of runtime (which means you have access to that metadata at runtime). When you call persist etc the persistence provider iterates through the fields (java.lang.reflect.Field) in your class and checks what annotations are present to build up your SQL statement. Try writing your own annotation and doing something with it. It won't seem very magical after that.
in your case annotation working means mapping with tablename with entity class is look like as ....
#Entity
#Table(name = "CompanyUser")
public class CompanyUserCAB implements java.io.Serializable
{
private long companyUserID;
private int companyID;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "companyUserID")
public long getCompanyUserID()
{
return this.companyUserID;
}
public void setCompanyUserID(long companyUserID)
{
this.companyUserID = companyUserID;
}
#Column(name = "companyID")
public int getCompanyID()
{
return this.companyID;
}
public void setCompanyID(int companyID)
{
this.companyID = companyID;
}
}