i've a base class that provide only identification:
public abstract class Identifable<T> {
#Id
private T id = null;
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
public boolean hasId() {
return id != null;
}
}
and several subclasses that extends it like:
#Entity
#Cache
public class MyEntity extends Identifable<String> {
/* some specific attributes and methods */
}
I get an java.lang.IllegalStateException: #Id field 'id' in com.mypkg.MyEntity must be of type Long, long, or String.
Why? Can't Objectify see the inherited #Id field?
Thanks
The cause:
Objectify only inspects types at runtime using reflection. Because of type erasure all unbounded type parameters are during compilation converted to Object type, which is what objectify sees and complains.
The solution:
Use concrete type for id field. Possibly move it to a child class, as proposed by #Anthony.
In JPA, you must use for a field marked with #Id one of the following types:
any Java primitive type; any primitive wrapper type; java.lang.String; java.util.Date; java.sql.Date; java.math.BigDecimal; java.math.BigInteger
Just remove the generics from the base class and use one of the mentioned types for your id field.
Let's reason about if for a while... You are trying to build a super type in which the type of the ID varies. Are you sure that this is what you want objectify to build (a hierarchy of objects in which the root entity has a unknown ID type)? While I've seen this kind of code in several ORM frameworks, this is how I would build what you want.
Interface (not part of the object hierarchy):
public interface Identifable<T> {
public T getId();
public void setId(T id);
public boolean hasId();
}
Root of your hiearchy implements Identifable with a concrete type for the id:
#Entity
public class MyBaseClass implements Identifable<String> {
#Id
private String id = null;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public boolean hasId() {
return id != null;
}
}
And subclass comes naturally out of it:
#EntitySubclass(index=true)
public class MyEntity extends MyBaseClass {
// fields, accessors and mutators
}
Related
I use MongoDBRepository in spring boot, and when I save some object in database everything is ok. but when I find object by id spring does not allow do that.
I try to change VehicleRoutingProblemSolution type to Object type, but VehicleRoutingProblemSolution have other object field PickupService and it without default constructor to. And yes, this class has immutable... I can't create default constructors, what can I do?
import com.fasterxml.jackson.annotation.JsonProperty;
import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection = "vrp_solutions")
public class VrpSolutionHolder {
// Specifies the solution id
#Id
#JsonProperty("id")
private String id;
// Specifies the solution id
#JsonProperty("solution")
private VehicleRoutingProblemSolution vehicleRoutingProblemSolution;
// Created at timestamp in millis
#JsonProperty("created_at")
private Long created_at = System.currentTimeMillis();
public VrpSolutionHolder(String id, VehicleRoutingProblemSolution vehicleRoutingProblemSolution) {
this.id = id;
this.vehicleRoutingProblemSolution = vehicleRoutingProblemSolution;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public VehicleRoutingProblemSolution getVehicleRoutingProblemSolution() {
return vehicleRoutingProblemSolution;
}
public void setVehicleRoutingProblemSolution(VehicleRoutingProblemSolution vehicleRoutingProblemSolution) {
this.vehicleRoutingProblemSolution = vehicleRoutingProblemSolution;
}
public Long getCreated_at() {
return created_at;
}
public void setCreated_at(Long created_at) {
this.created_at = created_at;
}
}
org.springframework.web.util.NestedServletException: Request
processing failed; nested exception is
org.springframework.data.mapping.model.MappingInstantiationException:
Failed to instantiate
com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution
using constructor NO_CONSTRUCTOR with arguments
I ran into the exact same problem. A persistent immutable class containing other class instances, throwing that aforementioned exception when retrieved by this repository method:
public interface ProjectCodeCacheRepository extends MongoRepository<CachedCode, String> {
public CachedCode findByCode(String code);
public List<CachedCode> findByClientId(UUID clientId);
}
...
List<CachedCode> cachedForClient = this.codeCacheRepo.`**findByClientId**`(clientId);
...
Following Erwin Smouts hints, this is nicely fixed by giving it a special constructor annotated org.springframework.data.annotation.PersistenceConstructor like so:
#Document(collection="cachedcodes")
public class CachedCode {
#PersistenceConstructor
public CachedCode(String code, UUID clientId, LocalDateTime expiration) {
this.code = code;
this.clientId = clientId;
this.expiration = expiration;
}
public CachedCode(String code, UUID clientId, long secondsExpiring) {
this.code = code;
this.clientId = clientId;
this.expiration = LocalDateTime.now().plusSeconds(secondsExpiring);
}
public UUID getClientId( ) {
return this.clientId;
}
public String getCode() {
return this.code;
}
public boolean hasExpired(LocalDateTime now) {
return (expiration.isBefore(now));
}
...
#Id
private final String code;
private final UUID clientId;
private final LocalDateTime expiration;
}
So, you should check if your VehicleRoutingProblemSolution has a) a constructor that matches the database fields (check in mongo client) and b) is annotated to be the one used by the driver (or whichever piece of Spring magic under the hood).
If your framework tool requires (visible) no-arg constructors (plus accompanying setters), and the class you have is required to stay as is, then you could roll your own, say, MutableVehicleRoutingProblemSolution where in the setters you could have :
this.vehicleRoutingProblemSolution = new VehicleRoutingProblemSolution(vehicleRoutingProblemSolution.getId(), newSolution);
Thus your MutableVehicleRoutingProblemSolution wraps around the existing VehicleRoutingProblemSolution.
Hacky smell to it, but it fits the requirements.
(Or you could try to find a tool that is able to use, not annotations on the contained fields, but annotations on constructor arguments.)
This is a problem where the corresponding class does not have a no-arg constructor like - I was facing an issue with java.io.File.
Solution:
In general - change the declaration to Object class and convert where we are using the class.
from
class MyClass{
File myfile;
}
to
class MyClass{
Object myFile;
}
For anyone using lombok, you need to remove the #Builder annotation on your class and use #Data instead, or follow the above solution to provide a specialized constructor
Oddly, I received this when I attempted to decorate a custom interface with ...
#Document(collection = "Person")
Example:
package test.barry.interfaces;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
#Document(collection = "Person")
public interface CustomRepository
{
void updatex(Query filterPredicate, UpdateDefinition updatePredicate);
}
I am using JDK 7.
I have an abstract class BaseEntity which is using generics:
public class BaseEntity<Id extends java.io.Serializable> {
protected Id id;
public BaseEntity() {
}
public Id getId() {
return id;
}
public void setId(Id id) {
this.id = id;
}
// also has hashCode() and equals() methods to be based on id
}
Now I want to create an interface whose type would be BaseEntity and I also want the Id of BaseEntity to be available in the interface. How can I do that?
I tried this code:
public interface BaseLookup<T extends BaseEntity> {
T findById(Id id);
}
but I got 2 messages:
1st one was on BaseEntity. The message was:
Base Entity is a raw type. References to generic type BaseEntity<Id>
should be parameterized.
2nd one was on "Id". The error message was:
Id cannot be resolved to a type.
What I am doing wrong here?
You have to specify the type of BaseEntity in BaseLookup. For better readability, I defind ID type as I.
You could use something like this:
class BaseEntity<I extends java.io.Serializable> {
protected I id;
public BaseEntity() {
}
public I getId() {
return id;
}
public void setId(I id) {
this.id = id;
}
// also has hashCode() and equals() methods to be based on id
}
interface BaseLookup<I extends java.io.Serializable, T extends BaseEntity<I>> {
T findById(I id);
}
public interface BaseLookup<Id, T extends BaseEntity<Id>> {
T findById(Id id);
}
Since you parameterized BaseEntity, you should always use a parameter whenever you reference BaseEntity. That first warning you got was telling you that you were using the BaseEntity (with no parameters) raw type, which is something that really only exists for legacy code and shouldn't be used. Since, ostensibly, BaseEntity is not just one type but an entire collection of types (one for each T), we need to tell it which BaseEntity we're extending, which amounts to adding an additional generic argument.
I am currently defining JPA entities for a legacy database (lots of composite keys, but also single-column keys). I have created the following entity superclass:
#MappedSuperclass
public abstract class AbstractEntity<ID extends Serializable> {
public abstract ID getId();
public abstract void setId(ID id);
}
And then a superclass for composite keys (as well as a superclass for long primary key, not listed here):
#MappedSuperclass
public abstract class AbstractEmbeddedIdEntity<ID extends Serializable> extends AbstractEntity<ID> {
#EmbeddedId
private ID id;
public AbstractEmbeddedIdEntity() {
id = newId();
}
#Override
public ID getId() {
return id;
}
#Override
public void setId(ID id) {
this.id = id;
}
protected abstract ID newId();
}
And finally concrete entities like this:
#Entity
#Table(name = "firstEntity")
public class FirstEntity extends AbstractEmbeddedIdEntity<FirstEntityId> {
public FirstEntity() {
}
#Embeddable
public static class FirstEntityId implements Serializable {
#Column(name = "firstId")
private String firstId;
public FirstEntityId() {
}
#Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof FirstEntityId)) {
return false;
}
FirstEntityId other = (FirstEntityId) obj;
return
Objects.equals(firstId, other.firstId);
}
#Override
public int hashCode() {
return Objects.hash(firstId);
}
}
#Override
protected FirstEntityId newId() {
return new FirstEntityId();
}
}
Now the issue is that if I have multiple entities like this and try to access an ID property of an entity (currently with Spring Boot, e.g. findByIdFirstId(String firstId)), an exception is thrown:
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [firstId] on this ManagedType [unknown]
I have debugged this and found out that in hibernate, the metamodel maps all of my entities to the same MappedSupperclass instance. During application startup, the #EmbeddedId returned by newId() is set to the MappedSupperclass, overwriting the ID of the previous entity. So in the end, all entities are mapped to the same MappedSupperclass, but the MappedSupperclass only has the #EmbeddedId of the last entity.
In the above example, accessing the ID property fails because the #EmbeddedId of the last entity doesn't have a property called "firstId" (it has been overwritten with the ID properties of the last entity).
Now I am wondering if my approach is wrong, if I am missing something or if this could be an issue with hibernate?
Complete example using spring boot available on github. Run with mvn spring-boot:run.
This looks to me like a bug in hibernate, therefore I have created a ticket in the hibernate bug tracker.
As a workaround I am now defining the ID attribute (#EmbeddedId) in the concreate entity classes instead of the abstract superclass.
I have an Entity class with a set of enums:
#Entity
public class Something {
public enum Type{
FIRST_ENUM((short)1),
SECOND_ENUM((short)2);
private short id;
private Type(short id) {
this.id = id;
}
public short getId() {
return id;
}
}
#CollectionTable(name="table_name", joinColumns=#JoinColumn(name="something_id"))
#Column(name="type")
private Set<Type> types;
... //other props + getters and setters
}
For the enum I made a converter (whole converter package is loaded by #EntityScan annotation) :
#Converter(autoApply=true)
public class TypeConverter implements AttributeConverter<Type, Integer> {
#Override
public Integer convertToDatabaseColumn(Type st) {
//implementation
}
#Override
public Type convertToEntityAttribute(Integer i) {
//implementation
}
}
Now when I try to use the enum in a query
... AND {packagename}.Something$Type.FIRST_ENUM MEMBER OF {someobject}.something.types ...
I stumble upon following error:
org.hibernate.QueryException: Unrecognized Hibernate Type for handling query constant ({package}.Something$Type.FIRST_ENUM); expecting LiteralType implementation or AttributeConverter
Does anyone got a clue why I cannot use the enum in my query? It somehow seems the enum is not known to Hibernate. I don't understand why, because the class is loaded when I start my application.
I have a generic class as follows:
public MyClass<T>{
T id;
public T getId(){return id;}
public void setId(T id){this.id=id;}
}
I instantiate it as follows:
MyClass<String> myClass = new MyClass<String>();
When looking the getId() method through reflection (i == the index of the getId method):
myClass.getClass().getMethods()[i].getReturnType();
It will say that the return type is java.lang.Object.
I'm guessing I'm asking the impossible here, but, would it somehow be possible for the class to actually say java.lang.String in this case?
The reason I'm asking this is because I'm using App engine and it's Datastore. A simplified scenario: I got all my classes inheriting from MyClass and therefore they get a id with the type T, where T is either Long, String or Key. But the datastore thinks no matter what that the id field is an java.lang.Object, which is prohibited. Do I need to make classes like MyClassString, MyClassLong etc, or is there some way i can bypass this?
Thank you!
EDIT: After searching for another issue I had after "solving" this. I found this question actually mentioning my exact problem.
The type arguments for the parameterized type are lost at runtime through a process known as type erasure. At runtime there is no way to determine the method returns a String, since the actual type arguments used are not available.
Generic Faq
One way to work around this would be implement a generic interface, which would allow the objects to be used in a polymorphic manner:
Identifiable.java
public interface Identifiable<T> {
T getId();
void setId(T t);
}
Person.java
public class Person implements Identifiable<String> {
private String id;
#Override
public String getId() {
return id;
}
#Override
public void setId(String t) {
this.id = t;
}
public static void main(String[] args) {
Person person = new Person();
Method method = person.getClass().getMethods()[1]; //prints getId
System.out.println(method.getName());
System.out.println(method.getGenericReturnType()); //prints String
}
}
You could do this by storing a class variable within and returning this on request.
i.e.
public MyClass<T>{
Class<T> clazz;
T id;
public T getId(){return id;}
public void setId(T id){this.id=id;}
MyClass(Class<T> clazz) {
this.clazz=clazz
}
public Class<T> getIdClass() { return clazz; }
}
Now to find out what type your class is you can just call getIdClass() on it and use that returned type. Generics will enforce that the type is set correctly when an instance of MyClass is created.
A little modification can be added to Tim B's solution, if you do not want parametrized constructor.
public class MyClass<T>{
Class<T> clazz;
T id;
public T getId(){return id;}
public void setId(T id){this.id=id;}
public Class<?> getIdClass() {
if (id != null) {
return id.getClass();
} else {
return Object.class;
}
}
}
You can check it by
MyClass<String> myClass = new MyClass<String>();
myClass.setId("abc");
System.out.println(myClass.getIdClass());