Current Version: Java version 20.0.1, Spring Boot 3.1.2
Problem Description: I’m encountering an issue when fetching data from the database using JPA. I have two entities, AccountsEntity and InvitationEntity, with a @OneToMany and @ManyToOne relationship.
AccountsEntity:
--------------------
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Set;
import org.hibernate.annotations.Where;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import co.cfly.fp.accounts.phonenumber.unassigned.TwilioUnassignedNumberEntity;
import co.cfly.fp.invitation.InvitationEntity;
import co.cfly.fp.profile.common.MembersEntity;
import co.cfly.fp.profile.registeredprofile.RegisteredProfileEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.PreUpdate;
import jakarta.persistence.Table;
import jakarta.persistence.Version;
@Entity
@Table(name = "tbl_account")
@Where(clause = "is_deleted = false")
public class AccountsEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue (strategy = GenerationType.IDENTITY)
@Column(name = "tbl_account_id_pk")
private Long accountId;
@Column(name = "account_name")
private String accountName;
@Enumerated(EnumType.STRING)
@Column(name = "account_type")
private AccountType accountType;
@Column(name = "account_image")
private String accountImage;
@OneToOne
@JoinColumn(name = "tbl_app_user_creator_id_fk", referencedColumnName = "tbl_app_user_id_pk")
private RegisteredProfileEntity appUserCreator;
@OneToOne
@JoinColumn(name = "tbl_app_user_owner_id_fk", referencedColumnName = "tbl_app_user_id_pk")
private RegisteredProfileEntity accountOwner;
@Column(name = "creation_date_time", columnDefinition = "TIMESTAMP", nullable = false)
@CreatedDate
private ZonedDateTime creationDateTime;
@Column(name = "last_modified_date_time", columnDefinition = "TIMESTAMP", nullable = false)
@LastModifiedDate
private ZonedDateTime updationDateTime;
@Column(name = "is_active")
private boolean isActive;
@Column(name = "created_by")
private Long createdBy;
@Column(name = "updated_by")
private Long updatedBy;
@Column(name = "is_deleted")
private boolean isDeleted;
@OneToMany(mappedBy = "account")
private List<MembersEntity> appMembers;
@OneToMany(mappedBy = "account")
private Set<InvitationEntity> invities;
@OneToMany(mappedBy = "accounts")
private Set<TwilioUnassignedNumberEntity> twilioUnassignedNumbers;
@Column(name = "version")
@Version
private Long version;
//Setters & Getters
//no arguments constructor
InvitationEntity:
-------------------
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import jakarta.persistence.*;
import org.hibernate.annotations.Where;
import co.cfly.fp.accounts.AccountsEntity;
import co.cfly.fp.call.conference.ConferenceEntity;
import co.cfly.fp.group.GroupEntity;
import co.cfly.fp.nonapp.NonAppUserEntity;
import co.cfly.fp.phonenumber.TwilioPhoneNumberEntity;
import co.cfly.fp.phonenumber.TwilioPhoneNumberType;
import co.cfly.fp.profile.registeredprofile.RegisteredProfileEntity;
import co.cfly.fp.util.common.ModuleType;
import co.cfly.fp.util.common.StatusTypeEnum;
@Entity
@Table(name = "tbl_invitation_app_and_nonapp")
@Where(clause = "is_deleted = false")
public class InvitationEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue (strategy = GenerationType.IDENTITY)
@Column(name = "tbl_invitation_id_pk")
private Long invitationId;
@Column(name = "status")
private StatusTypeEnum statusTypeEnum;
@Enumerated(EnumType.STRING)
@Column(name = "module_type")
private ModuleType moduleType;
@Column(name = "sms")
private String sms;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "tbl_account_id_fk")
private AccountsEntity account;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "tbl_conference_id_fk")
private ConferenceEntity conference;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "tbl_group_id_fk")
private GroupEntity group;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "tbl_twilio_phone_number_id_fk")
private TwilioPhoneNumberEntity twilioPhoneNumberEntity;
@Enumerated(EnumType.STRING)
@Column(name = "twilio_phone_number_type")
private TwilioPhoneNumberType twilioPhoneNumberType;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "tbl_app_user_sender_fk")
private RegisteredProfileEntity sender;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "tbl_app_user_reciever_fk")
private RegisteredProfileEntity reciever;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "tbl_non_app_user_id_fk")
private NonAppUserEntity nonAppUser;
@Column(name = "creation_date_time", columnDefinition = "TIMESTAMP", nullable = false)
private ZonedDateTime creationDateTime;
@Column(name = "last_modified_date_time", columnDefinition = "TIMESTAMP", nullable = false)
private ZonedDateTime updationDateTime;
@Column(name = "is_active")
private boolean isActive;
@Column(name = "created_by")
private Long createdBy;
@Column(name = "updated_by")
private Long updatedBy;
@Column(name = "is_deleted")
private boolean isDeleted;
@Column(name = "twilio_sender_reciept")
private String twilioSenderReciept;
@Column(name = "reinvite_count")
private int reInviteCount;
//setters & getters
//no arguments constructor
AccountsServiceImpl:
----------------------------
public Set<Accounts> findAllByAppUserId() {
log.info("findAllByAppUserId : method call started");
UserDetailsImpl userDetails = fetchUserDetailsFromToken();
String userId = userDetails.getId().toString();
RegisteredProfileEntity requestingUser = rps.findUser(userId);
Set<AccountsEntity> accountsEntitiesForMember = requestingUser.getMembers().stream()
.map(member -> member.getAccount()).filter(account -> null != account && account.isActive())
.collect(Collectors.toSet());
Set<AccountsEntity> accountsEntitiesForAdmin = accountsRepo.findAllByAccountOwnerAndIsActiveTrue(requestingUser)
.orElse(null);
Set<AccountsEntity> accountsEntitiesForMemberAndAdmin = new HashSet<AccountsEntity>();
if (!FoneappUtil.isNullOrEmpty(accountsEntitiesForMember))
accountsEntitiesForMemberAndAdmin.addAll(accountsEntitiesForMember);
if (!FoneappUtil.isNullOrEmpty(accountsEntitiesForAdmin))
accountsEntitiesForMemberAndAdmin.addAll(accountsEntitiesForAdmin);
if (FoneappUtil.isNullOrEmpty(accountsEntitiesForMemberAndAdmin))
return null;
accountsEntitiesForMemberAndAdmin.remove(null);
log.info("findAllByAppUserId : method call completed");
return AccountsMapper.mapper.toJsons(accountsEntitiesForMemberAndAdmin);
}
AccountsMapperImpl:
----------------------------
public Set<Accounts> toJsons(Set<AccountsEntity> accountsEntities) {
if ( accountsEntities == null ) {
return null;
}
Set<Accounts> set = new LinkedHashSet<Accounts>( Math.max( (int) ( accountsEntities.size() / .75f ) + 1, 16 ) );
for ( AccountsEntity accountsEntity : accountsEntities ) {
set.add( toJson( accountsEntity ) );
}
return set;
}
public Accounts toJson(AccountsEntity accountsEntity) {
if ( accountsEntity == null ) {
return null;
}
Accounts accounts = new Accounts();
Long appUserId = accountsEntityAccountOwnerAppUserId( accountsEntity );
if ( appUserId != null ) {
accounts.setAccountOwnerId( String.valueOf( appUserId ) );
}
accounts.setAccountName( accountsEntity.getAccountName() );
accounts.setAccountType( accountsEntity.getAccountType() );
accounts.setAccountImage( accountsEntity.getAccountImage() );
if ( accountsEntity.getCreatedBy() != null ) {
accounts.setCreatedBy( String.valueOf( accountsEntity.getCreatedBy() ) );
}
accounts.setCreationDateTime( TimeMapper.updateTimeFormat( accountsEntity.getCreationDateTime() ) );
accounts.setUpdationDateTime( TimeMapper.updateTimeFormat( accountsEntity.getUpdationDateTime() ) );
if ( accountsEntity.getAccountId() != null ) {
accounts.setAccountId( String.valueOf( accountsEntity.getAccountId() ) );
}
accounts.setVersion( accountsEntity.getVersion() );
accounts.setActive( accountsEntity.isActive() );
bindMembers( accountsEntity, accounts );
return accounts;
}
@AfterMapping
@Named("AccountMembers")
default void bindMembers(AccountsEntity accountsEntity, @MappingTarget Accounts accounts) {
List<MembersEntity> members = accountsEntity.getAppMembers();
if (null != members) {
members.removeIf(member -> null == member.getAccount());
accounts.setAppMembers(RegisteredProfileToClientTransformer.transformer.registeredEntitiesToClientProfiles(
members.stream().filter(member -> null == member.getTwilioPhoneNumberEntity())
.map(member -> member.getRegisteredProfileEntity()).collect(Collectors.toSet())));
}
if (null != accountsEntity.getInvities())
accounts.setNonAppMembers(accountsEntity.getInvities().stream()
.filter(invite -> null == invite.getTwilioPhoneNumberEntity()).map(invite -> invite.getNonAppUser())
.map(NonAppUserMapper.mapper::toJson).collect(Collectors.toSet()));
}
NonAppUserMapper:
---------------------------
import java.util.Set;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import co.cfly.fp.profile.registeredprofile.RegisteredProfileMapper;
import co.cfly.fp.util.mapper.IgnoreUnmappedMapperConfig;
import co.cfly.fp.util.utitlity.TimeMapper;
@Mapper(uses = { TimeMapper.class , RegisteredProfileMapper.class }, config = IgnoreUnmappedMapperConfig.class)
public interface NonAppUserMapper {
NonAppUserMapper mapper = Mappers.getMapper(NonAppUserMapper.class);
@Mapping(source = "firstName", target = "name")
@Mapping(source = "email", target = "mailId")
NonAppUser toJson(NonAppUserEntity entity);
Set<NonAppUser> toJsons(Set<NonAppUserEntity> entity);
@InheritInverseConfiguration
NonAppUserEntity toEntity(NonAppUser json);
}
AccountsMapper:
----------------------
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.mapstruct.AfterMapping;
import org.mapstruct.BeanMapping;
import org.mapstruct.IterableMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import co.cfly.fp.nonapp.NonAppUserMapper;
import co.cfly.fp.profile.common.MembersEntity;
import co.cfly.fp.profile.registeredprofile.RegisteredProfileMapper;
import co.cfly.fp.profile.transformer.RegisteredProfileToClientTransformer;
import co.cfly.fp.util.mapper.IgnoreUnmappedMapperConfig;
import co.cfly.fp.util.utitlity.TimeMapper;
@Mapper(uses = { TimeMapper.class, RegisteredProfileMapper.class },config = IgnoreUnmappedMapperConfig.class)
public interface AccountsMapper {
public AccountsMapper mapper = Mappers.getMapper(AccountsMapper.class);
@Mapping(source = "accountsEntity.accountOwner.appUserId", target = "accountOwnerId")
@Mapping(target = "appMembers", ignore = true) // Ignoring appMembers and NonAppMembers to avoid incorrect values
@Mapping(target = "nonAppMembers", ignore = true)
@BeanMapping(qualifiedByName = "AccountMembers")
Accounts toJson(AccountsEntity accountsEntity);
Set<Accounts> toJsons(Set<AccountsEntity> accountsEntities);
@AfterMapping
@Named("AccountMembers")
default void bindMembers(AccountsEntity accountsEntity, @MappingTarget Accounts accounts) {
List<MembersEntity> members = accountsEntity.getAppMembers();
if (null != members) {
members.removeIf(member -> null == member.getAccount());
accounts.setAppMembers(RegisteredProfileToClientTransformer.transformer.registeredEntitiesToClientProfiles(
members.stream().filter(member -> null == member.getTwilioPhoneNumberEntity())
.map(member -> member.getRegisteredProfileEntity()).collect(Collectors.toSet())));
}
if (null != accountsEntity.getInvities())
accounts.setNonAppMembers(accountsEntity.getInvities().stream()
.filter(invite -> null == invite.getTwilioPhoneNumberEntity()).map(invite -> invite.getNonAppUser())
.map(NonAppUserMapper.mapper::toJson).collect(Collectors.toSet()));
}
@Mapping(source = "accountsEntity.accountOwner.appUserId", target = "accountOwnerId")
@Mapping(target = "appMembers", ignore = true) // Ignoring appMembers and NonAppMembers to avoid incorrect values
@Mapping(target = "nonAppMembers", ignore = true)
@Named(value = "AccountEntityToJson")
Accounts entityToJsonWithoutMember(AccountsEntity accountsEntity);
@IterableMapping(qualifiedByName = "AccountEntityToJson")
Set<Accounts> entityToJsonWithoutMembers(Set<AccountsEntity> accountsEntities);
}
Note: Code breaks at,
if (null != accountsEntity.getInvities())
accounts.setNonAppMembers(accountsEntity.getInvities().stream()
.filter(invite → null == invite.getTwilioPhoneNumberEntity()).map(invite → invite.getNonAppUser())
.map(NonAppUserMapper.mapper::toJson).collect(Collectors.toSet()));
Has anyone encountered a similar issue or have any insights into what might be causing this discrepancy? Any help would be greatly appreciated! Thank you.