Search code examples
mysqljsonspring-mvcgsonorika

Exception with Orika Mapper + Gson (JSON) + MySQL


I'm trying to implement some kind of "poll system" on my web based on Spring MVC + MySQL. The problem I'm having is that I have 2 classes I'm trying to map. One is the DTO which I use to move the data through the web, and the other one is the one I use to access the database with Hibernate. So far, so good.

The idea I'm trying to implement is having a few Lists on the DTO, transform them into a JSON and afterwards saving them on the database (as a String).

Now, in order to map these 2 objects I'm using an Orika Custom Map. The funny thing that happens to me is that I'm able to save the lists I have into the database as JSON (I'm using GSON to convert them), but when I need them back, Orika throws and exception.

I'll try now to put my code as clear as possible to see if someone can help me. As I'm telling you, when the time comes to save the data, I have no problem at all, but when I need to get it out (getting the string, parsing it to a JSON and afterwards to a List), it collapses. Any ideas?? Thanks in advance

ORIKA MAPPING

mapperFactory.classMap(EncuestaDTO.class, Encuesta.class).mapNulls(false).byDefault().customize(new CustomMapper<EncuestaDTO, Encuesta>(){

    @Override
    public void mapAtoB(EncuestaDTO a, Encuesta b, MappingContext context){
        Type listTypeString = new TypeToken<List<String>>(){}.getType();
       /************ Respuestas */
        String respuestas = new Gson().toJson(a.getRespuestas(), listTypeString);
        b.setRespuestas(respuestas);
    }

    @Override
    @SuppressWarnings("unchecked")
    public void mapBtoA(Encuesta b, EncuestaDTO a, MappingContext context){

        Type listTypeString = new TypeToken<List<String>>(){}.getType();
        /************ Respuestas */
        List<String> respuestas = new Gson().fromJson(b.getRespuestas(), listTypeString);
        a.setRespuestas(respuestas);

    }

}).register();

Encuesta

@Id
@Column(name = "idEncuesta", unique = true, nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idEncuesta;
@Column(name = "idCreador", nullable = false)
private Long idCreador;
@Column(name = "pregunta")
private String pregunta;
@Column(name = "respuestas")
private String respuestas;
@Column(name = "numRespuestas")
private Long numRespuestas;
@Column(name = "fechaCreacion")
private Date fechaCreacion;
@Column(name = "fechaFin")
private Date fechaFin;
@Column(name = "oficial")
private boolean oficial;

EncuestaDTO

private Long idEncuesta;
private Long idCreador;
private String pregunta;
private List<String> respuestas;
private Long numRespuestas;
private Date fechaCreacion;
private Date fechaFin;
private boolean oficial;

MANAGER

@Override
public void aniadirEncuesta(EncuestaDTO encuestaDTO) {
    encuestaRepositorio.aniadirEncuesta(mapper.map(encuestaDTO, Encuesta.class));
}

@Override
public List<EncuestaDTO> getListaEncuestas() {
    List<Encuesta> listaEncuesta = (List<Encuesta>) encuestaRepositorio.getListaEncuestas();
    return mapper.mapAsList(listaEncuesta, EncuestaDTO.class); // HERE I GET THE ERROR
}

EXCEPTION

GRAVE: El Servlet.service() para el servlet [appServlet] en el contexto con ruta [/myApp] lanzó la excepción [Request processing failed; nested exception is ma.glasnost.orika.MappingException: While attempting the folling mapping:
sourceType = String
destinationType = ArrayList<String>
Error occurred: ma.glasnost.orika.MappingException: While attempting the folling mapping:
sourceType = ArrayList<String>
sourceProperty = 1(String)
destinationType = String
destinationProperty = bytes(byte[])
Error occurred: java.lang.IllegalArgumentException: Attempt was made to generate assignment/setter code for [destination.bytes(byte[])] which has no setter/assignment method] con causa raíz
java.lang.IllegalArgumentException: Attempt was made to generate assignment/setter code for [destination.bytes(byte[])] which has no setter/assignment method
at ma.glasnost.orika.impl.generator.VariableRef.assign(VariableRef.java:223)
at ma.glasnost.orika.impl.generator.specification.ObjectToObject.generateMappingCode(ObjectToObject.java:23)
at ma.glasnost.orika.impl.generator.SourceCodeContext.mapFields(SourceCodeContext.java:644)
at ma.glasnost.orika.impl.generator.MapperGenerator.generateFieldMapCode(MapperGenerator.java:252)
at ma.glasnost.orika.impl.generator.MapperGenerator.addMapMethod(MapperGenerator.java:172)
at ma.glasnost.orika.impl.generator.MapperGenerator.build(MapperGenerator.java:72)
at ma.glasnost.orika.impl.DefaultMapperFactory.buildMapper(DefaultMapperFactory.java:1046)
at ma.glasnost.orika.impl.DefaultMapperFactory.lookupMapper(DefaultMapperFactory.java:614)
at ma.glasnost.orika.impl.DefaultMapperFactory.lookupMapper(DefaultMapperFactory.java:581)
at ma.glasnost.orika.impl.MapperFacadeImpl.resolveMapper(MapperFacadeImpl.java:523)
at ma.glasnost.orika.impl.MapperFacadeImpl.resolveMappingStrategy(MapperFacadeImpl.java:203)
at ma.glasnost.orika.impl.DefaultBoundMapperFacade$BoundStrategyCache.getStrategy(DefaultBoundMapperFacade.java:253)
at ma.glasnost.orika.impl.DefaultBoundMapperFacade.map(DefaultBoundMapperFacade.java:136)
at ma.glasnost.orika.generated.Orika_Encuesta_EncuestaDTO_Mapper137715823.mapBtoA(Orika_Encuesta_EncuestaDTO_Mapper137715823.java)
at ma.glasnost.orika.impl.ReversedMapper.mapAtoB(ReversedMapper.java:65)
at ma.glasnost.orika.impl.mapping.strategy.UseCustomMapperStrategy$ForwardMapperReference.map(UseCustomMapperStrategy.java:74)
at ma.glasnost.orika.impl.mapping.strategy.UseCustomMapperStrategy.map(UseCustomMapperStrategy.java:50)
at ma.glasnost.orika.impl.MapperFacadeImpl.mapAsCollection(MapperFacadeImpl.java:633)
at ma.glasnost.orika.impl.MapperFacadeImpl.mapAsList(MapperFacadeImpl.java:386)
at ma.glasnost.orika.impl.MapperFacadeImpl.mapAsList(MapperFacadeImpl.java:716)
at ma.glasnost.orika.impl.ConfigurableMapper.mapAsList(ConfigurableMapper.java:180)

MySQL TABLE

CREATE TABLE `encuestas` (
   `idEncuesta` int(11) NOT NULL AUTO_INCREMENT,
   `idCreador` int(11) NOT NULL,
   `pregunta` longtext,
   `respuestas` longtext,
   `numRespuestas` int(11) DEFAULT NULL,
   `fechaCreacion` datetime DEFAULT NULL,
   `fechaFin` datetime DEFAULT NULL,
   `oficial` tinyint(1) DEFAULT NULL,
   PRIMARY KEY (`idEncuesta`),
   UNIQUE KEY `idEncuesta_UNIQUE` (`idEncuesta`),
   KEY `idCreador_idx` (`idCreador`),
   CONSTRAINT `idCreador` FOREIGN KEY (`idCreador`) REFERENCES `usuarios` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Solution

  • OK, I finally got it. The problem was that Orika tried to map it byDefault and, afterwards, tried to do the custom mapping. That meant that it crashed as it didn't know how to map it by default.

    By adding and ".exclude("respuestas")" and later on configure the mapping manually (as shown in the code), I got everything mapped just as I wanted. In terms of code, it would be like this:

    mapperFactory.classMap(EncuestaDTO.class, Encuesta.class).mapNulls(false).exclude("respuestas").byDefault().customize(new CustomMapper<EncuestaDTO, Encuesta>()