45

This is a follow up to the following question on the JPA Transient annotation Why does JPA have a @Transient annotation?

I have a transient variable that I do not want to persist and it is marked with the transient annotation. However, when I want to produce JSON from my rest controller, this transient variable is not available in the outputted JSON.

The POJO PublicationVO is straight forward with no fancy attributes, just some private attributes (that are persisted) with getters and setters and 1 transient variable.

@RequestMapping(value = { "{publicationId}"}, method = RequestMethod.GET, produces = "application/json")
@ResponseBody public PublicationVO getPublicationDetailsJSON(@PathVariable(value = "publicationId") Integer publicationId) {
    LOG.info("Entered getPublicationDetailsJSON - publicationId: " + publicationId);

    //Call method to get the publicationVO based on publicationId
    PublicationVO publicationVO = publicationServices.getPublicationByIdForRestCalls(publicationId);       
    LOG.info("publicationVO:{}", publicationVO);

    LOG.info("Exiting getPublicationDetailsJSON");
    return publicationVO;
}

The PublicationVO is as follows

    package com.trinity.domain.dao;

import java.util.Calendar;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Transient;

import com.fasterxml.jackson.annotation.JsonInclude;

@Entity
@Table(name = "publication")
public class PublicationVO {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    private Integer id;    
    @Column(name = "publicationName", unique = false, nullable = false, length = 200)
    private String publicationName;
    @Column(name = "publicationSource", unique = false, nullable = false, length = 45)
    private String publicationSource;

    @Column(name = "dateAdded", unique = false, nullable = false)
    private Calendar dateAdded;

    @Transient
    private float percentageProcessed;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getPublicationName() {
        return publicationName;
    }

    public void setPublicationName(String publicationName) {
        this.publicationName = publicationName;
    }

    public String getPublicationSource() {
        return publicationSource;
    }

    public void setPublicationSource(String publicationSource) {
        this.publicationSource = publicationSource;
    }

    public Calendar getDateAdded() {
        return dateAdded;
    }

    public void setDateAdded(Calendar dateAdded) {
        this.dateAdded = dateAdded;
    }

    public float getPercentageProcessed() {
        return percentageProcessed;
    }

    public void setPercentageProcessed(float percentageProcessed) {
        this.percentageProcessed = percentageProcessed;
    }

    @Override
    public String toString() {
        return "PublicationVO [id=" + id + ", publicationName=" + publicationName + ", publicationSource=" + publicationSource + ", dateAdded=" + dateAdded
                + ", percentageProcessed=" + percentageProcessed + "]";
    }
}

When I see the debug statement for publicationVO in my logs, the transient variable is included in the output but in my client code, the transient variable is not included in the json response.

Any help is greatly appreciated.

Thank you, Damien

Community
  • 1
  • 1
Damien Gallagher
  • 981
  • 4
  • 13
  • 25
  • Can you edit your question and publish the code of PublicationVO ? Does your transient variable not being serialized have a proper getter ? – m4rtin Sep 07 '14 at 19:37
  • @m4rtin - i just included the publicationVo class. And yes the transient variable has a getter method – Damien Gallagher Sep 07 '14 at 19:43
  • As I can see some Spring MVC annotations as well as the Spring tag in your question, can you confirm that the serialization to JSON is done by the Jackson library ? – m4rtin Sep 07 '14 at 19:54
  • yes sorry the serialization is done via jackson. If its a case I need to have the field non transient I can try and figure out a way to work around it but was hoping there is a cleaner way – Damien Gallagher Sep 07 '14 at 20:01
  • Well the thing is, in Java, **transient** really means that a property should not be serialized. JPA simply "extended" the meaning to prevent property persistence. But in your case, the property is simply JPA-annotated and not transient in a *core-java* way and I would really be surprised of Jackson taking care of JPA annotations. Plus the base mechanism of Jackson sits on getters/setters and your property has proper getter/setter so... Maybe the problem is not related to the @Transient annotation of the property ? – m4rtin Sep 07 '14 at 20:15
  • 1
    true m4rtin thats a fair point. Ill try adding the database column manually, remove the transient annotation and see does it make any difference and get back to you – Damien Gallagher Sep 07 '14 at 20:16
  • just tried removing the transient annotation, adding the db column for the transient field and it works fine. – Damien Gallagher Sep 07 '14 at 20:19
  • You mean the *percentageProcessed* property effectively gets serialized by Jackson in the response body of your getPublicationDetailsJSON method when it wasn't the case before ? – m4rtin Sep 07 '14 at 20:22
  • thats corret m4rtin. When the property doesnt have the Transient annotation and I added the column for percentageProcessed, the percentageProcessed property was serialized in the response – Damien Gallagher Sep 07 '14 at 20:29
  • Are you using Hibernate as your ORM ? If yes, I think I found something and I'll write you answer. If not, which ORM do you use ? (if any) – m4rtin Sep 07 '14 at 20:38
  • yes I am using Hibernate m4rtin - thanks for your help – Damien Gallagher Sep 07 '14 at 21:12
  • @DamienGallagher Wait ... you are saying that with your `@Transient` variable is not null when retrieve from DB? So your percentageProcessed still shows? – Eric Huang May 17 '17 at 06:30

10 Answers10

58

I simply added JsonSerialize and JsonDeserialize annotations.

@Transient
@JsonSerialize
@JsonDeserialize
private String myField;
Fidan Hakaj
  • 6,818
  • 3
  • 30
  • 33
  • 3
    Thanks, it did the trick. This helps to define separately what should be persisted in the database and what should be put in the JSON. – stefitz Aug 04 '17 at 08:17
  • 2
    I don't know if it works, but it seems like we end up mixing cross-layer information in the entity. Transient is for db layer (lowest layer) while JsonSerialize should be at UI/Client layer. – Padmarag Dec 27 '17 at 15:52
  • I had to make sure I imported the correct packages for the `@Json*` Annotations. `org.springframework.cloud.cloudfoundry.com.fasterxml.jackson.databind.annotation` did NOT work, but `com.fasterxml.jackson.databind.annotation.` did. – N4ppeL Sep 26 '19 at 12:59
32

Contrary to what I was telling you in comments, it seems that Jackson does care about JPA annotations when used to serialize instances of entity classes thanks to the Jackson's Hibernate module.

Within that module, there is an HibernateAnnotationIntrospector that the documentation refers to as a

simple AnnotationIntrospector that adds support for using Transient to denote ignorable fields (alongside with Jackson and/or JAXB annotations).

And as you can see here, the default behavior of Jackson is to check for any @Transient annotation it can find.

So in the end, your problem can be solved in either of those 3 ways :

  1. Configure Jackson (using HibernateAnnotationIntrospector's setUseTransient method) to disable the check for @Transient annotations (see this answer for implementation details).
  2. Use another object than PublicationVO as the returned result of your getPublicationDetailsJSON method. You'll have to copy properties from your value object to the object being returned at some point.
  3. Remove the @Transient annotation and persist the property (but I would understand if that is not an option for you since you probably have good reason to have made this property JPA-transient in the first place).

Cheers

Community
  • 1
  • 1
m4rtin
  • 2,445
  • 22
  • 34
22

Just to add further to the answer provided by m4rtin

I went with the first approach - Configure Jackson (using HibernateAnnotationIntrospector's setUseTransient method) to disable the check for @Transient annotations.

In my project I follwed had to follow the following thread to avoid jackson serialization on non fetched lazy objects Avoid Jackson serialization on non fetched lazy objects

To configure my project to not ignore transient annotations, I set up the Hibernate4Module as follows

        Hibernate4Module hm = new Hibernate4Module();
    hm.disable(Feature.USE_TRANSIENT_ANNOTATION);

Thanks for your help on this m4rtin

Community
  • 1
  • 1
Damien Gallagher
  • 981
  • 4
  • 13
  • 25
  • Glad you found the implementation details regarding the first approach, I'll edit my answer to add a reference to your answer which will definitely be helpful to people going for solution 1. – m4rtin Sep 09 '14 at 12:02
  • Finally something that works! I created a [gist](https://gist.github.com/evonsdesigns/a79f81868fe43ca32ee4eb7341d8b635) with the various files necessary for this setup. – Gaʀʀʏ Apr 16 '18 at 15:23
15

I use both @Transient and @JsonProperty, then it works!

@Transient
@JsonProperty
private String mapImageSrc;
Pang
  • 9,564
  • 146
  • 81
  • 122
Clarke
  • 151
  • 1
  • 2
  • 1
    +1 Yes this works as well. JsonSerialize and JsonProperty both seem to work. I wonder if there are subtle differences and if one if preferred over the other. – HopeKing Jun 16 '17 at 06:23
  • 1
    You can even refine the access using @JsonProperty(access = Access.WRITE_ONLY) or @JsonProperty(access = Access.READ_ONLY) – Selindek Aug 24 '17 at 07:30
  • This is the only thing worked for me, when using @Transient for the my field.. – hashcoder Sep 29 '20 at 07:18
6

What it worked for me:

@Transient
@JsonProperty

in the GETTER (not in the private field definition)

AND

@JsonAutoDetect(fieldVisibility = Visibility.ANY) 

annotating the class

Alexander
  • 4,420
  • 7
  • 27
  • 42
Vic
  • 337
  • 3
  • 9
4

Since no other solution here worked for me, let me post my solution how it did the trick for me:

@Transient
@JsonSerialize
private String mapImageSrc;

This seems to be the best solution to me as the @JsonSerialize annotation has been made for that use case.

dave0688
  • 5,372
  • 7
  • 33
  • 64
  • In my case I only have getter, such as int getCounter() , with no setter nor field. All I have to do just adding @JsonSerialize only. Without transient or others – Harun Aug 09 '21 at 07:16
2

I had the same problem. Below solution worked for me:

@Bean
public Module hibernate5Module() {
    Hibernate5Module hnetModule = new Hibernate5Module();
    hnetModule.disable(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION);
    return hnetModule;
}

Thanks to m4rtin.

Alok Singh
  • 488
  • 2
  • 6
1

Try @JsonView after adding @Transient

Sapna
  • 11
  • 1
0

In my case only works with this:

@Transient
@JsonGetter(value = "transientProperty")
public String getSomething() {
    return something;
}

I hope this can be useful for someone. Regards.

hizmarck
  • 686
  • 9
  • 19
-1

use @JsonIgnore in com.fasterxml.jackson.annotation there also is @JsonFormat for Date variable. works for me