Using embedded value objects in spring data jpa pr

2019-03-03 15:32发布

I'm working with Spring Boot 2.0RC2 and in the documentation I read you can return a projection of an entity instead of the entity as a whole when calling the Repository. This is working fine in case I use a String in my Entity but not when I use an embedded value objects.

Let's say I have the Product entity:

@Entity
@Table(name = "t_product")
public class Product extends BaseEntity {

    @Column(nullable = false)
    private String name;

    private Product() {}

    private Product(final String name) {
        this.name = name;
    }

    public static Result<Product> create(@NonNull final String name) {
        return Result.ok(new Product(name));
    }

    public String getName() {
        return name;
    }

    public void setName(@NonNull final String name) {
        this.name = name;
    }
}

The BaseEntity simply holds the id, created and updated attributes.

I have my projection interface called ProductSummary:

interface ProductSummary {
    String getName();
    Long getNameLength();
}

And in my ProductRepository I have the following method that returns the ProductSummary:

public interface ProductRepository extends JpaRepository<Product, Long> {
    @Query(value = "SELECT p.name as name, LENGTH(p.name) as nameLength FROM Product p WHERE p.id = :id")
    ProductSummary findSummaryById(@Param("id") Long id);
}

This works perfectly fine. Now let's say I am doing DDD and instead of using a String to represent the name attribute in the Product entity, I want to use a value object called Name:

@Embeddable
public class Name implements Serializable {

    public static final int MAX_NAME_LENGTH = 100;

    @Column(nullable = false, length = Name.MAX_NAME_LENGTH)
    private String value;

    private Name() {}

    private Name(final String value) {
        this.value = value;
    }

    public static Result<Name> create(@NonNull final String name) {
        if (name.isEmpty()) {
            return Result.fail("Name cannot be empty");
        }

        if (name.length() > MAX_NAME_LENGTH) {
            return Result.fail("Name cannot be longer than " + MAX_NAME_LENGTH + " characters");
        }

        return Result.ok(new Name(name));
    }

    public String getValue() {
        return value;
    }
}

I change my Product entity to:

@Entity
@Table(name = "t_product")
public class Product extends BaseEntity {

    @Embedded
    private Name name;

    private Product() {}

    private Product(final Name name) {
        this.name = name;
    }

    public static Result<Product> create(@NonNull final Name name) {
        return Result.ok(new Product(name));
    }

    public Name getName() {
        return name;
    }

    public void setName(final Name name) {
        this.name = name;
    }
}

And in the ProductSummary I change the return type from String to Name.

When I run that I always get the exception:

Caused by: java.lang.IllegalAccessError: tried to access class com.acme.core.product.ProductSummary from class com.sun.proxy.$Proxy112

Can I make this work or am I missing some restriction which doesn't allow this?

1条回答
Bombasti
2楼-- · 2019-03-03 15:53

If you wish to get the complete Name field(not a particular field in Name class), then you need to create another interface like ProductSummary.

interface ProductSummary {
    NameSummary getName();

    interface NameSummary {
      String getValue();
    }
}

No need to change anything in your repository.

It is quite clearly documented here

And make sure your interfaces and the methods are public.

查看更多
登录 后发表回答