I have the following example in which i have a separate domain layer and a separate persistence layer. I am using Mapstruct for mapping and I get StackOverflow when mapping from domain to entity or from entity to domain because of the bidirectional reference that always gets called on -> infinite loop scenario. How can I use Mapstruct for this scenario?
class User {
private UserProfile userProfile;
}
class UserProfile {
private User user;
}
@Entity
class UserEntity {
@OneToOne
@PrimaryKeyJoinColumn
private UserProfileEntity userProfile;
}
@Entity
class UserProfileEntity {
@OneToOne(mappedBy = "userProfile")
private UserEntity userEntity;
}
class for mapping is pretty basic
@Mapper
interface UserMapper {
UserEntity mapToEntity(User user);
User mapToDomain(UserEntity userEntity);
}
Check out the Mapstruct mapping with cycles example.
A solution to your problem is also demonstrated in the documentation for Context annotation .
A complete example: https://github.com/jannis-baratheon/stackoverflow--mapstruct-mapping-graph-with-cycles .
Mapper:
@Mapper
public interface UserMapper {
@Mapping(target = "userProfileEntity", source = "userProfile")
UserEntity mapToEntity(User user,
@Context CycleAvoidingMappingContext cycleAvoidingMappingContext);
@InheritInverseConfiguration
User mapToDomain(UserEntity userEntity,
@Context CycleAvoidingMappingContext cycleAvoidingMappingContext);
@Mapping(target = "userEntity", source = "user")
UserProfileEntity mapToEntity(UserProfile userProfile,
@Context CycleAvoidingMappingContext cycleAvoidingMappingContext);
@InheritInverseConfiguration
UserProfile mapToDomain(UserProfileEntity userProfileEntity,
@Context CycleAvoidingMappingContext cycleAvoidingMappingContext);
}
where CycleAvoidingMappingContext
keeps track of the already mapped objects and reuses them avoiding the stack overflow:
public class CycleAvoidingMappingContext {
private final Map<Object, Object> knownInstances = new IdentityHashMap<>();
@BeforeMapping
public <T> T getMappedInstance(Object source,
@TargetType Class<T> targetType) {
return targetType.cast(knownInstances.get(source));
}
@BeforeMapping
public void storeMappedInstance(Object source,
@MappingTarget Object target) {
knownInstances.put(source, target);
}
}
Mapper usage (mapping single object):
UserEntity mappedUserEntity = mapper.mapToEntity(user, new CycleAvoidingMappingContext());
You can also add a default method on your mapper:
@Mapper
public interface UserMapper {
// (...)
default UserEntity mapToEntity(User user) {
return mapToEntity(user, new CycleAvoidingMappingContext());
}
// (...)
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.