Nanny level one-stop service -- self association to construct parent-child relationship (@ JsonBackReference and @ JsonManagedReference solve circular dependency)


I, an unknown rookie, was urged to change (it was already in May and I haven't fulfilled my promise 🥴 But I remember, because I was flattered 🤣). So next, I decided to explode my liver for seven days and seven nights and give him a few boutiques (draw a big cake first) 😑)

Finally, I'm not a rookie learning series, but I'm still the rookie in the past. There's no change. The only change is the gradual reduction of hair.

A few days ago, I was hit by writing code. The menu classification function I wrote in two days was re implemented by a colleague in the group in two hours 🙄, Look at the beauty of other people's code, and then look at me, 💩💩💩 A piece of shit 💩💩💩.

I haven't seen you for half a year, and there's a little more nonsense. Next, I'll attach some explanations to this beautiful code and share it with you (my shit code won't show up here)


It is inevitable to encounter some requirements for hierarchical display of goods or menus in business. These menu management involves hierarchical classification. Let me take you through the process first (because I am also a rookie, I know that you may not know much about some annotations involved in the code, so I will explain them one by one later. After understanding the code, you can safely and boldly use CtrlCV skills!)

Construction classification entity

After the entity is constructed, the parent-child structure relationship of classification and hierarchy is realized. In the future, new implementations can be expanded according to business requirements.

@Table(name = "tb_category")
@GenericGenerator(name = "category-uuid", strategy = "uuid")
public class Category extends BaseEntity {

    @GeneratedValue(generator = "category-uuid")
    private String categoryId;

    private String categoryName;

    // Self association to realize parent-child relationship
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "parent_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
    @NotFound(action= NotFoundAction.IGNORE)
    private Category parent;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Category> childNodes;

    public String getParentId() {
        if (this.parent == null) return "";
        return this.parent.getCategoryId();

    public Long getCreationTime() {
        if(this.getCreationDate()==null) return 0L;
        return this.getCreationDate().getTime();
    public Long getLastUpdatedTime() {
        if(this.getLastUpdatedDate()==null) return 0L;
        return this.getLastUpdatedDate().getTime();

    private Integer categoryLevel = 1;

    private String categoryDescription;

    private String categoryIconName;

    private Integer status;

    public static final String CATEGORY_VALID = "valid";
    public static final String CATEGORY_ID = "categoryId";

    // Add parent node
    public void addParentCategory(Category parentCategory) {
        setCategoryLevel(parentCategory.getCategoryLevel() + 1);

    // Add child node
    public void addChildren(Category child) {
        if (this.childNodes == null) {
            this.childNodes = Lists.newArrayList();

    public boolean hasChildren() {
        return getChildNodes().size() > 0;

    // Get the child node down (excluding yourself)
    public List<Category> getChildNodes() {
        if (this.childNodes == null) return Lists.newArrayList();
        return Collections.unmodifiableList( -> x.getValid() != 0).collect(Collectors.toList()));

    // Get the child node id downward (excluding itself)
    public List<String> getChildIds() {
        if (this.childNodes == null) return Lists.newArrayList();
        return Collections.unmodifiableList( -> x.getValid() != 0).map(Category::getCategoryId).collect(Collectors.toList()));


The following are the implementation methods of some common requirements:

// Gets the current node and all parent nodes
public List<Category> findCategoryAndAllParents(List<Category> categories, Category category) {
    // First add the current node to the collection
    if (StringUtils.isNotEmpty(category.getParentId())) {
        Category parent = getCategoryById(category.getParentId());
        // Recursively look up the parent node
        findCategoryAndAllParents(categories, parent);
    return categories;

// Get the current node id and all its subset node IDs
public List<String> getChildrensAndSelfIds(String categoryId) {
    Category category = getCategoryById(categoryId);
    List<String> ids = new ArrayList<>();
    // Add yourself to the collection first
    return getSelfIdAndAllChildIds(ids, category);
private List<String> getSelfIdAndAllChildIds(List<String> ids,Category category){
    // Add all child node IDs of the current node to the collection
    // Traverse to find the child nodes of the current node, and recursively add the IDs of their child nodes to the collection
    return ids;

Annotation interpretation

  • @JsonBackReference:

    • Annotation used to indicate that associated property is part of two-way linkage between fields; and that its role is "child" (or "back") link. Value type of the property must be a bean: it can not be a Collection, Map, Array or enumeration.

      The official API document is described above. Its general meaning is: this annotation is used to declare the relationship between two attributes with two-way connection. The @ JsonBackReference annotation is used to "child" link. (that is, the annotation is defined in the child role, and a small chestnut will be given below). It must be marked on a bean and cannot be a container such as a collection.

    • Linkage is handled such that the property annotated with this annotation is not serialized; and during deserialization, its value is set to instance that has the "managed" (forward) link

  • @JsonManagedReference:

    • Annotation used to indicate that annotated property is part of two-way linkage between fields; and that its role is "parent" (or "forward") link. Value type (class) of property must have a single compatible property annotated with JsonBackReference.

      @The JsonManagedReference annotation is used for the "parent" link. (that is, the annotation is defined in the parent role, and a small chestnut will be given below). The value type corresponding to this attribute must be marked with the @ JsonBackReference annotation

    • Linkage is handled such that the property annotated with this annotation is handled normally (serialized normally, no special handling for deserialization); it is the matching back reference that requires special handling (this paragraph will be uniformly explained in the difference between @ JsonBackReference and @ JsonManagedReference below)

  • To make it easier to understand the @ JsonBackReference and @ JsonManagedReference annotations, let's take a simple example to use these two annotations (I used the parent-child relationship constructed by self correlation above)

    // This is a one to many example of teachers and students (a teacher has multiple students, and multiple students correspond to a teacher. It is equivalent to that the teacher is the father and the student is the son)
    public class Teacher {
        private String name;
        private Integer age;
        @JsonManagedReference // Defined in parent role
        private List<Student> students;
    public class Student {
        private String name;
        private Integer age;
        @JsonBackReference // Defined in a child role and labeled on a parent attribute
        private Teacher teacher;

@Difference between JsonBackReference and @ JsonManagedReference

When Jackson serializes an object, if there is a circular dependency in the object (i.e. mutual dependency between teachers and students above), stack overflow will be reported. The @ JsonBackReference and @ JsonManagedReference are mainly used to solve the circular dependency problem of one-to-many and many-to-one relationships. This pair of annotations is a sharp tool to solve the circular dependency between parents and children.

  • You can see from the official API document that the attributes marked with @ JsonBackReference will be ignored when serializing (i.e. converting objects into json data) (that is, the json data in the result does not contain the content of this attribute). The attributes marked with @ JsonManagedReference will be sequenced.

  • Only when @ JsonBackReference and @ JsonManagedReference are used together, the attribute value of @ JsonBackReference annotation can be automatically injected during deserialization.

  • @JsonIgnore: directly ignore a property to break infinite recursion. Serialization or deserialization are ignored.

  • @ManyToOne: indicates that the current entity is one to many.

    • @JoinColumn: configure foreign keys
      • Name: foreign key field name, used to identify the name of the corresponding field in the table
  • @OneToMany: indicates that the current entity is one end of a one to many relationship.

    • mappedBy: relationship maintenance

      • mappedBy = "parent" means that the parent attribute in the Category class is used to maintain the relationship. This name must be exactly the same as the name of the parent attribute in the Category
      • OneToMany must write mappedBy. One to many and many to one relationships may also generate a useless intermediate table to associate the two. However, we generally do not recommend using intermediate tables. Using mapperBy can avoid the system generating intermediate tables (adding a field to record foreign keys in a multi party database)

      Cascade: cascade operation

      • CascadeType. PERSIST cascade persistence (save) operation
      • CascadeType. MERGE cascade update (merge) operation
      • CascadeType. REFRESH cascades refresh operations, which only query and obtain operations
      • CascadeType. REMOVE cascade delete operation
      • CascadeType. ALL cascades all the above operations

      fetch: load type. By default, one party is immediate load and the other party is delayed load

      • FetchType.LAZY lazy load
      • Fetchtype.eagle loads immediately (default)
  • @JsonInclude: this annotation is only useful for serialization operations. It is used to control whether methods, attributes, etc. should be serialized

      • ALWAYS: the default policy is to perform serialization in any case
      • NON_NULL: not null
      • NON_EMPTY: null, collection array, etc. without content, empty string, etc. will not be serialized
      • NON_DEFAULT: if the field is the default value, it will not be serialized
  • @JsonProperty: used on a property or method to serialize the name of the property into another name

  • @NotFound: ignored when referenced foreign key data is not found

    In many to one and one to one relationships, one party introduces the attribute of the other party. If the referenced attribute value data is not found in the database, hibernate will throw an exception by default. To solve this problem, add @ NotFound annotation

Partial reference:

Tags: Java

Posted on Fri, 01 Oct 2021 19:14:39 -0400 by mrmufin