In my repository layer, I'm trying to return a DTO called "ItemBase" which contains about half of the fields from an entity "Item".
For example, this is my Item class
@Document("Item")
public class Item {
Long itemId;
String itemDetail;
String itemScore;
String itemColor;
//Getters and Setters
}
And here is my ItemBase class
public class ItemBase {
private final String itemDetail;
private final String itemScore;
public ItemBase() { //sets all to null }
public ItemBase(String itemDetail, String itemScore) { ... }
//Getters only
}
And finally, what I have for my Repo class
@Repository
public interface ItemRepo extends MongoRepository<Item, String> {
@Query(value = "{'itemId': ?0}", fields= "{ 'itemDetail': 1, 'itemScore': 1, '_id': 0}
ItemBase findOneByItemIdIncludeEmbeddedFields(Long itemId);
}
I know that my repo method findOneByItemIdIncludeEmbeddedFields() works with the rest of my code. I checked this by changing my DTO to be mutable, and created setters for each variable. However, by nature of a DTO, I want these variables to be final.
So when the class immutable, I get this error which basically tells me I don't have a setter:
...threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException:
Cannot set property itemDetail because no setter, no wither and it's not part of the persistence constructor public ItemBase()
I know there's a way to do this for SpringJPA and SQL like so:
@Query("select new ItemBase(i.itemDetail, i.itemScore) from Item i where i.itemId = ?0")
ItemBase findOneByItemIdIncludeEmbeddedFields(Long itemId);
And that would work perfectly fine with an immutable ItemBase, though I'm not sure if the new ItemBase() constructor is possible with MongoDB, or how the syntax is for it.
One solution: I was hoping for a way to do this with only the ItemRepo interface like how it can be done above with JPA and a
new ItemBase()inside the @Query. I did not find that out, however, I will post the solution I am using.I've created a ItemRepoCustom and moved the function into there without a @Query, with a shorter name as well since it'll be custom implemented anyway (findOneByItemIdIncludeEmbeddedFields -> findItemBase).
Then, in an implementation class:
Finally, just modify ItemRepo to extend ItemRepoCustom as well.
Not as succinct as I would have liked it to be, but it works.