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?
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.
No need to change anything in your repository.
It is quite clearly documented here
And make sure your interfaces and the methods are public.