Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix " Failed to instantiate 'className' using constructor NO_CONSTRUCTOR with arguments" in immutable class

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

like image 276
tambovflow Avatar asked Dec 07 '22 12:12

tambovflow


2 Answers

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).

like image 104
onouv Avatar answered Jan 08 '23 03:01

onouv


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.)

like image 30
Erwin Smout Avatar answered Jan 08 '23 02:01

Erwin Smout