Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you send back a subset of a JPA entity that is owned by another entity?

Tags:

java

json

openjpa

I have an entity that owns another entity:

//psuedocode    
public class ClassA{
   private String name;

   @OneToOne
   private ClassB classb;
}

public class ClassB{
   private String thing1;
   private String thing2;
   private String thing3;
}

When I retrieve ClassA objects, I don't want to see ClassB.thing3, but I do want to see thing1 and thing 2:

{
"name":"classa",
"classb":{
         "thing1":"hi",
         "thing2":"there"
        }
}

But if I query for ClassB I want to see everything:

{"thing1":"hi",
 "thing2":"there",
 "thing3":"joseph"}

So I can't just put an ignore annotation over thing3, because then I'll ignore it on the second fetch. I tried a Converter<ClassB>, but that forces me to implement toString() and fromString() for the JSON, which dies on converting the JSON object to Java-side (the converter expects a String, but gets the object instead).

I want to avoid building/parsing the JSON object myself if possible to let my json provider do the work, if possible. I'm on Johnzon.

like image 288
GuitarStrum Avatar asked Jan 31 '17 23:01

GuitarStrum


3 Answers

This is possible, you need to use @NamedEntityGraph,

This should help, http://www.thoughts-on-java.org/jpa-21-entity-graph-part-1-named-entity/

like image 167
zillani Avatar answered Nov 01 '22 19:11

zillani


Something like this should be possible by querying using SELECT NEW, but you're going to need some new Classes for that ... and won't be passing your entities directly to JSON.

new Classes: (pseudocode)

class ResultB {
   String thing1;
   String thing2;

   public ResultB(ClassB classB) {
       this.thing1 = classB.thing1;
       this.thing2 = classB.thing2; 
   } 
}

class ResultA {
   String name;
   ResultB resultB;

   public ResultA(ClassA classA) {
      this.name=classA.name;
      this.resultB=new ResultB(classA);
   }
}

Query:

select new ResultA(a) from ClassA a fetch join a.classB;

Then you can pass ResultA instead of ClassA to JSON.

PS: As mentioned in the comment above, I don't think NamedEntityGraphs are the way to go here

like image 35
tom Avatar answered Nov 01 '22 19:11

tom


I would always fetch all the data from the database and let the filtering to be done by the JSON provider if you want to serialize it anyway. If you use Jackson you can simply add views to your fields:

public class ClassA {

   @JsonView(Views.AlwaysInclude.class)
   private String name;

   @JsonView(Views.AlwaysInclude.class)
   @OneToOne
   private ClassB classb;
}

public class ClassB {
   @JsonView(Views.AlwaysInclude.class)
   private String thing1;

   @JsonView(Views.AlwaysInclude.class)
   private String thing2;

   private String thing3;
}

public class Views {
    public static class AlwaysInclude {
    }
}

Later when you serialize your object you just need to use your view:

String result = mapper
  .writerWithView(Views.AlwaysInclude.class)
  .writeValueAsString(new ClassA());

When you want to serialize only ClassB then you shouldn't use views.

String result = mapper.writeValueAsString(new ClassB());
like image 29
akos.bordas Avatar answered Nov 01 '22 19:11

akos.bordas