Kakawait
Kakawait

Reputation: 4029

JPA persist entity with refreshing nested entity strategy

I'm questioning myself to how could I perform that following scenario:

Let's take a sample entity Item

@Data
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@Entity
class Item implements Serializable {
    @Id
    @GeneratedValue
    @Setter(AccessLevel.NONE)
    private UUID id;

    private String name;

    @ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.REFRESH})
    @JoinTable(name = "item_category", joinColumns = {@JoinColumn(name = "item_id", referencedColumnName = "id")},
            inverseJoinColumns = {@JoinColumn(name = "category_id", referencedColumnName = "id")})
    private Set<Category> categories;

    @PrePersist
    protected void generateUuid() {
        id = UUID_GENERATOR.generate();
    }
}

and Category

@Data
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@Entity
class Category implements Serializable {
    @Id
    @GeneratedValue
    @Setter(AccessLevel.NONE)
    private UUID id;

    private String name;

    @PrePersist
    protected void generateUuid() {
        id = UUID_GENERATOR.generate();
    }
}

In my use case, Item should have many Category but Item can't directly create or update Category, it must use existing Category (image a drop down list of categories managed by administrator). That why Cascade.PERSIST and Cascade.MERGE are missing.

Now imagine following API POST /api/items with such JSON request structure

{
    "name": "new item",
    "categories": [
        "6d126e36-a7a5-11e7-abc4-cec278b6b50a",
        "88d5a052-a7a5-11e7-abc4-cec278b6b50a"
    ]
}

That should create a new items with existing Category with given ids (consider that ids are correct and exist).

However I would return response that contain complet nested entity values:

{
    "name": "new item",
    "categories": [
        {
            "id": "6d126e36-a7a5-11e7-abc4-cec278b6b50a",
            "name": "CAT_A"
        },
        {
            "id": "88d5a052-a7a5-11e7-abc4-cec278b6b50a",
            "name": "CAT_B"
        }
    ]
}

To avoid boilerplate, imagine that Dto and DtoToEntityMapper (and reverse) are present and working as expected.

The problem is when I will persist() new Item entity produces from JSON:

Item(
  id=null,
  name="new item",
  Categories:[
    Category(
      id="6d126e36-a7a5-11e7-abc4-cec278b6b50a",
      name=null
    ),
    Category(
      id="6d126e36-a7a5-11e7-abc4-cec278b6b50a",
      name=null
    )
  ]
)

Every below instance are detached.

Problem is after persist() every Category instance seems to be detached (or at least name is still equals to null).

So what's the best way to retrieve Category fields?

  1. Should I use refresh(item) after persist(item)?
  2. Should I use attached Category instead of detached and thus fetching each Category using id before persist(item)?
  3. May I use same representation for request (POST request body) and response, thus name will be pre-filled. But what's happen if id is correct but name is not (since I don't accept Category update from Item)? Simply ignore name or raise exception?
  4. Other way

In addition if 2. is best solution, where should I fetch those nested entities? I mean in Controller (presentation layer), in DtoToEntityMapper (presentation layer), in Service (service layer)


PS: Please I would stick to JPA specification and not using Hibernate specific features. PS2: I would be able to do in single POST request

Upvotes: 0

Views: 1058

Answers (1)

fhossfel
fhossfel

Reputation: 2191

Using entityManager.merge() instead of entityManager.persist() should do the trick. It will return the managed instance so accessing its categories should give you the categories including the description.

Note: Be sure that you use the managed instance after the call.

Upvotes: 1

Related Questions