0

In a department class, it has a parent department and multiple sub-departments. I hope that the entity that JPA queries from the database contains the parent department information and sub-department information, but the parent department information does not need to contain the information of its sub-department, and the sub-department collection does not need to contain the parent department information.

I am trying to add annotations on the parent department genus, but it does not work. The entity type returned by JPA is HibernateProxy, i am trying add HibernateProxyTypeAdapter to gson and add this code

new GsonBuilder().excludeFieldsWithoutExposeAnnotation().registerTypeAdapterFactory(HibernateProxyTypeAdapter.FACTORY)

finaly, i am get result from postman is {}, yes, an empty object. this is not what i want.

this is department class:

@Setter
@Getter
@Entity
@Table(name = "department")
public class Department implements Serializable {

    private static final long serialVersionUID = -3360650322926476819L;

    @Id
    @Column(name = "id", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "SnowFlakeIdGenerator")
    @GenericGenerator(name = "SnowFlakeIdGenerator", strategy = "cn.lmt.id.SnowFlakeIdGenerator")
    private Long id;

    @Column(name = "create_time", updatable = false)
    private LocalDateTime createTime;

    @Column(name = "update_time", insertable = false)
    private LocalDateTime updateTime;

    @PrePersist
    private void onPersist() {
        this.setCreateTime(LocalDateTime.now());
    }

    @PreUpdate
    private void onUpdate() {
        this.setUpdateTime(LocalDateTime.now());
    }

    @Column(name = "dept_name", length = 16)
    private String deptName;

    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Department parent;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    private List<Department> children;
}
public class HibernateProxyTypeAdapter extends TypeAdapter<HibernateProxy> {

   public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
       @Override
       @SuppressWarnings("unchecked")
       public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
           return (HibernateProxy.class.isAssignableFrom(type.getRawType()) ? (TypeAdapter<T>) new HibernateProxyTypeAdapter(gson) : null);
       }
   };
   private final Gson context;

   private HibernateProxyTypeAdapter(Gson context) {
       this.context = context;
   }

   @Override
   public HibernateProxy read(JsonReader in) throws IOException {
       throw new UnsupportedOperationException("Not supported");
   }

   @SuppressWarnings({"rawtypes", "unchecked"})
   @Override
   public void write(JsonWriter out, HibernateProxy value) throws IOException {
       if (value == null) {
           out.nullValue();
           return;
       }
       // Retrieve the original (not proxy) class
       Class<?> baseType = Hibernate.getClass(value);
       // Get the TypeAdapter of the original class, to delegate the serialization
       TypeAdapter delegate = context.getAdapter(TypeToken.get(baseType));
       // Get a filled instance of the original class
       Object unproxiedValue = ((HibernateProxy) value).getHibernateLazyInitializer()
               .getImplementation();
       // Serialize the value
       delegate.write(out, unproxiedValue);
   }
}

I want parent and children information, but I don't want partment.chidren and children.parent information.

  • Your question is not so clear. If your problem is circular reference please go through the `@JsonManagedReference` & `@JsonBackReference` to solve the issue. You can refer the following answer https://stackoverflow.com/a/37394318/7458887. – Alexpandiyan Chokkan Jul 13 '19 at 11:52
  • @AlexpandiyanChokkan I am very sorry, English is my second language. My expression is not very clear.I solved this problem, but I still appreciate your answer. – Coloured_Glaze Jul 13 '19 at 13:44
  • You still can manually map your `Department` entity to intermediate DTO (say DepartmentDTO) with desired fields only, since having separate DTO layer is considered a good architecture practice in web applications. To reduce boilerplate code in mappings (like `dto.deptName = entity.deptName`) you can use tools like [MapStruct](http://mapstruct.org/) that will auto generate that code for you – Nikolai Shevchenko Jul 13 '19 at 14:49

2 Answers2

0

Let's say, you cannot have your cake and eat it!

JPA can manage the cyclic connection but the whole thing stops making sense when you serialize it.

So you have to skip serialization of one of the 2 fields!

@ManyToOne
@JoinColumn(name = "parent_id")
//@JsonIgnore @XmlTransient //commented out   ------------------------1
private Department parent;

@JsonIgnore @XmlTransient  //optionally comment out this line instead of 1 ---2
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Department> children;
Comfort Chauke
  • 126
  • 1
  • 9
  • I did it. Ignore its children attributes at the parent and ignore the parent attributes at the children. add @JsonIgnoreProperties("children") and @JsonIgnoreProperties("parent") for parent and children. But still thank you very much – Coloured_Glaze Jul 13 '19 at 13:37
0

Change the code to

    @ManyToOne
    @JoinColumn(name = "parent_id")
    @JsonIgnoreProperties("children")
    private Department parent;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    @JsonIgnoreProperties("parent")
    private List<Department> children;

and it works!