How can I query child entities from parent JHipster 4.6 Elastic - jhipster

I have a parent child relation ship.
#Entity
**#Table(name = "candidate")**
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "candidate",type = "candidate")
public class Candidate implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
**#OneToMany(mappedBy = "candidate")
#Field(type= FieldType.Nested)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<CandidateEducation> educations = new HashSet<>();**
I am able to save the data in Spring elastic using JHipster.
However when I search for candidate using its id in using candidateSearchRepository the result does not return the associated educations.
public interface CandidateSearchRepository extends ElasticsearchRepository<Candidate, Long> {
}
The other way works fine i.e. if I search for candidate Id in education document candidate document is embedded in the response.
public interface CandidateEducationSearchRepository extends ElasticsearchRepository<CandidateEducation, Long> {
}
I want to retrieve all associated child entities for the parent. i.e. using candidate id retrieve associated education entities.

Related

failed to lazily initialize a collection of role for a Map<Integer,String>

I have the follwoing entity
#Entity
#Table(name = "Parent")
public class Parent {
#ElementCollecion(fetch = FetchType.Eager)
Map<Integer,String> myMap;
public Map<Integer,String> getMyMap() {
return this.myMap;
}
}
Then somewhere in the code I have the follwoing
parent.getMyMap().get(someKey);
This line consistently throws a LazyInitialization exception. I have set the fetchType to eagerly load the collection so that the values are available at all times. What have I missed> Is it because of the access of the getMap.GetKey call?
I dont see any more information in the debug logs or anywhere else.
Generally the #ElementCollection is accompanied with a #CollectionTable along with #MapKeyColumn & #Column annotation to provide the table and values to load the content for the map.
#Entity
#Table(name = "Parent")
public class Parent {
#ElementCollection(fetch = FetchType.Eager)
#CollectionTable(name="example_attributes", joinColumns=#JoinColumn(name="example_id"))
#MapKeyColumn(name="name")
#Column(name="value")
Map<Integer,String> myMap;
public Map<Integer,String> getMyMap() {
return this.myMap;
}
}

Handling aggregate root

I'm new in DDD so I'm doing some practice to undertand a little bit more. I have Course BC with the follow rules:
Course has to be created first and then they can create the modules of one course
Every module will be finished by the user when he upload the homework
The course will be finished by the user when he finished all the modules
Definition:
A course covers a particular topic and it is comprised of module. For instance, sap course has 10 modules such as: module 1: what is it?, module 2: how to use it?…
After this, I realize that Course is the aggregate root of module, because the modules are finished I have to close the status of user with the course.
the model would be:
public class Course : AggregateRoot
{
private string title;
private List<Module> modules;
}
but also module is an aggregate root of homework because when the user upload his homework the module has to be closed. This make me think that this approach is wrong because is not possible in DDD have nested aggregate root. Someone knows what is it wrong?
[UPDATED]
Ok, now I understand how is work and why you split it in 2 BC. However I did some changes and some questions come to my mind.
-I've created enroll method as static and I put the constructor as private.
-Course have to be an array because one student can have more than one.
-I've put more parameters related with the course and also the teacher. Is the teacher and entity of course, right?
-I created status of course to update it when the module is finished this way I don't have to read all the modules to know it. is ok?
-How can I pass more information for every module like title and description? and is the course entity how create all the modules, right?
public class StudentEnrolment: AggregateRoot
{
private StudentId studentId;
private Course courses;
private constructor(
StudentId studentId,
Course course,
){
this.studentId= studentId;
this.courses[] = course;
}
public statuc function enroll(
StudentId studentId,
CourseId courseId,
string courseTitle,
string courseLink,
string teacherId,
string teacherName,
List<Tuple<ModuleId, string>> modules) {
teacher = new Teacher(...);
courseStatus = new courseStatus();
new course(courseTitle, courseLink, courseStatus, teacher);
return new self(studentId, course);
}
public function void uploadModuleHomework(ModuleId moduleId, Homework homework){
/* forward to course.uploadModuleHomework */
}
public boolean isCourseFinished(){
/* forward to course.isFinished */
}
public List<Tuple<ModuleId, string>> getModules(){
/* forward to course.getModules */
}
}
There are two different sub-domains (so we have two bounded contexts):
1.Courses and modules administration where the teachers can administer those; Here Course and Module can be Aggregate roots and a course could hold references to the Modules IDs (not to instances!).
public class Course: AggregateRoot
{
private string title;
private List<ModuleId> modules;
}
2.Student participations to the courses. Here there is a StudentEnrolment Aggregate root that contains references to the Course and Module from the other BC but as Value objects; it models the student participation to a single course; in this bounded context there is a new Entity, Homework, that track the student homework-upload and course participation status.
public class StudentEnrolment: AggregateRoot
{
private StudentId studentId;
private Course course;
private List<Homework> homeworks;
// initialize a student enrolment as public constructor or make constructor private and use a static method
// here is important to notice that only this AR creates its entities, it does not receive them as parameter
public constructor(
StudentId studentId,
Course course,
List<Module> modules){
this.studentId = studentId;
this.course = course;
//build the the homeworks entity list based on the modules parameter
//for each module create a Homework entity, that initially is not uploaded, like:
this.homeworks = modules.map(module => new Homework(module))
}
public function void uploadFileForHomework(ModuleId moduleId, string file){
/* find the homework by module Id and upload file*/
}
public boolean isCourseFinished(){
/*returns true if all homeworks are uploaded*/
/*optimization: you could have a status that is updated when a homework's file is uploaded*/
}
public List<Tuple<ModuleId, string, boolean>> getHomeworks(){
/* returns a list of readonly Homeworks, i.e. Tuple<ModuleId, string /*module title*/, boolean /*is uploaded*/> */
}
}
public class Homework: Entity
{
private Module module;
private string file;
public constructor(Module module){
this.module = module;
}
public void upload(string file){ this.file = file;}
public boolean isUploaded(){return (boolean)this.file;}
public string getUploadedFile(){return this.file;}
public ModuleId getModuleId(){return this.module.getId();}
}
public class Course: ValueObject
{
private string title;
private CourseId id;
public constructor(id, title){...}
public string getTitle(){return this.title;}
public string getId(){return this.title;}
}
public class Module: ValueObject
{
private string title;
private string description;
private ModuleId id;
public constructor(id, title, description){...}
public string getTitle(){return this.title;}
public string getDescription(){return this.description;}
public string getId(){return this.title;}
}
If you need to query the Enrolment to get the homeworks you should not return a list of Homeworks because the client code would think that it can call Homework.upload(file) directly, which is not permitted (only the Aggregate root can modify its internal entities). Instead, you could return a Tuple or better, you can create an immutable version of the Homework class.

Mapping a DTO in a DTO to entity in entity with mapstruct

I have a situation where I have another DTO in inside a DTO to which I have to map to its corresponding entity.
I am using mapstruct and I already have AnotherEntityMapper already existing.
DTO
public class EntityDTO {
private AnotherEntityDTO anotherEntityDTO;
// other fields
}
Entity
#Entity
public class Entity {
private AnotherEntity anotherEntity;
// other fields
}
How to change the EntityMapper interface, so that I can map anotherEntityDTO to anotherEntity?
Thanks.
It really depends which version of MapStruct you are using. If you are using 1.2.0.Beta or higher they you can just define the nested properties on your EntityMapper interface:
#Mapper
public interface EntityMapper {
#Mapping(target = "anotherEntity", source = "anotherEntityDTO")
#Mapping(target = "anotherEntity.propE", source = "anotherEntityDTO.propD")
Entity map(EntityDDTO dto);
}
Another option (and a must if you are using version less than 1.2.0.Beta) is to add a new Method in your EntityMapper like:
#Mapper
public interface EntityMapper {
#Mapping(target = "anotherEntity", source = "anotherEntityDTO")
Entity map(EntityDDTO dto);
#Mapping(target = "propE", source = "propD")
AnotherEntity map(AnotherEntityDTO);
}
or you can define a new Mapper AnotherEntityMapper for the AnotherEntity and use #Mapper(uses = {AnotherEntityMapper.class}):
#Mapper
public interface AnotherEntityMapper {
#Mapping(target = "propE", source = "propD")
AnotherEntity map(AnotherEntityDTO);
}
#Mapper(uses = {AnotherEntityMapper.class}
public interface EntityMapper {
#Mapping(target = "anotherEntity", source = "anotherEntityDTO")
Entity map(EntityDDTO dto);
}
It really depends on your use case. If you need to do mappings between AnotherEntity and AnotherEntityDTO on other places, I would suggest to use a new interface so you can reuse it where you need it

What layer is responsible for implementing a LazyLoading strategy for children objects of an entity

Let's say you have an order as an aggregate root. An order contains one or more line items.
It is my understanding that it's the repository's responsibility to instantiate an order object when asked.
The line items can be loaded at the time of the order object's creation (eager loaded), or the line item collection can be populated when it is accessed by the client code (lazy loaded).
If we are using eager loading, it's seems that the repository code would take responsibility with hydrating the line items when the order is created.
However if we are using lazy loading, how is the repository called when the LineItems collection is accessed without creating a dependency on the repository from the order domain class?
Main problem is in Repository's ability to get only aggregate roots (presenting aggregates), thus you cannot use Repository to get line items. This can lead to aggregate encapsulation violation.
I propose something like:
//Domain level:
public interface IOrderItemList {
IEnumerable<OrderItem> GetItems();
}
public class Order {
private IOrderItemList _orderItems;
public IEnumerable<OrderItem> OrderItems
{ get { return _orderItems.GetItems() } };
public Order(IOrderItemList orderItems)
{
_orderItems = orderItems;
}
}
public class OrderItemList : IOrderItemList
{
private IList<OrderItem> _orderItems;
public IEnumerable<OrderItem> GetItems() {
return _orderItems; //or another logic
}
//other implementation details
}
//Data level
public class OrderItemListProxy : IOrderItemList
{
//link to 'real' object
private OrderItemList _orderItemList;
private int _orderId;
//alternatively:
//private OrderEntity _orderEntity;
//ORM context
private DbContext _context;
public OrderItemListProxy(int orderId, DbContext context)
{
_orderId = orderId;
_context = context;
}
public IEnumerable<OrderItem> GetItems() {
if (_orderItemList == null)
{
var orderItemEntities = DbContext.Orders
.Single(order => order.Id == _orderId).OrderItems;
var orderItems = orderItemEntites.Select(...);
//alternatively: use factory to create OrderItem from OrderItemEntity
_orderItemList = new OrderItemList(orderItems);
}
return _orderItemList.GetItems();
}
}
public class OrderRepository
{
//ORM context
private DbContext _context;
Order GetOrder(int id)
{
var orderEntity = _context.Single(order => order.Id == id);
var order = new Order(new OrderItemListProxy(id, _context))
//alternatively:
//var order = new Order(new OrderItemListProxy(orderEntity, _context))
...
//init other fields
...
}
//other methods
...
}
Most important here is that IOrderItemList corresponds to domain layer, but OrderItemListProxy corresponds to data layer.
Finally,
You may use IList<OrderItem> instead of custom IOrderItemList or another appropriate interface.
Proxy implementation may differ.
I don't provide best practicies for using db context, it may depend on technologies you use.

Creating object in entity after all instance variables were set by Eclipse Link

The following JPA entity is given:
#Entity(name = "MyEntity")
public class MyEntity {
#Id
protected String id = null;
protected String name = null;
protected String adress = null;
#Transient
protected User user = new User(name, adress);
// Required by JPA
protected MyEntity() {
}
public MyEntity(String id, String name, String adress) {
// assign instance variables
}
public String getUser() {
return user.toString();
}
...
}
When getUser() will be called on MyEntity created by Eclipse Link the desired String will not be returned. The problem is that User was instantiated before the instance variables of MyEntity were set by Eclipse Link. In other words User was created with name = null and adress = null.
How can I ensure that User will be created after Eclipse Link has set all instance variables?
You can use the #PostLoad annotation like this:
#PostLoad
private initUser(){
user = new User(name, adress);
}
The method will be executed once your entity is fully loaded and it's fields are set.

Resources