Problem in updating the entity with unidirectional #ManyToOne relation in SpringBoot application - java

I have two entities as below and the main problem is when I want to update the AccountRequestStatus entity. I save the integer enum code of AccountRequestStatusEnum in the database to persist the AcountRequest status in the whole application.
AccountRequestStatusEnum
public enum AccountRequestStatusEnum {
INITIAL(0),
SUCCESS(1);
private final Integer type;
AccountRequestStatusEnum(Integer type) {
this.type = type;
}
public Integer getType() {
return type;
}
public static AccountRequestStatusEnum of(Integer type) {
for (AccountRequestStatusEnum accountRequestStatusEnum : AccountRequestStatusEnum.values()) {
if (type.equals(accountRequestStatusEnum.getType()))
return accountRequestStatusEnum;
}
return null;
}
}
AccountRequest
#Entity
#Table(name = "T_ACCOUNT_REQUEST", uniqueConstraints = {#UniqueConstraint(columnNames = {"ACCOUNT_NO", "MESSAGE_ID"})})
#SequenceGenerator(
name = "SEQ_T_ACCOUNT_REQUEST",
sequenceName = "SEQ_T_ACCOUNT_REQUEST",
allocationSize = 1)
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode(callSuper = false)
#ToString
public class AccountRequest extends AbstractAuditingEntity {
private Long id;
private String messageId;
private String issuer;
private EventType type;
private EventName name;
private String accountNo;
private LocalDateTime dateTime;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_T_ACCOUNT_REQUEST")
#Column(name = "ID", nullable = true, insertable = true, updatable = true, precision = 0)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "MESSAGE_ID")
public String getMessageId() {
return messageId;
}
public void setMessageId(String messageId) {
this.messageId = messageId;
}
#Column(name = "ISSUER")
public String getIssuer() {
return issuer;
}
public void setIssuer(String issuer) {
this.issuer = issuer;
}
#Transient
public EventType getType() {
return type;
}
public void setType(EventType type) {
this.type = type;
}
#Column(name = "TYPE")
public Integer getEventTypeCode() {
if (Objects.nonNull(type)) {
return type.getType();
} else return null;
}
public void setEventTypeCode(Integer typeCode) {
type = EventType.of(typeCode);
}
#Transient
public EventName getName() {
return name;
}
public void setName(EventName name) {
this.name = name;
}
#Column(name = "NAME")
public Integer getEventNameCode() {
if (Objects.nonNull(name)) {
return name.getType();
} else return null;
}
public void setEventNameCode(Integer type) {
name = EventName.of(type);
}
#Column(name = "ACCOUNT_NO")
public String getAccountNo() {
return accountNo;
}
public void setAccountNo(String accountNo) {
this.accountNo = accountNo;
}
#Column(name = "DATE_TIME")
public LocalDateTime getDateTime() {
return dateTime;
}
public void setDateTime(LocalDateTime dateTime) {
this.dateTime = dateTime;
}
}
AccountRequestStatus
#Entity
#Table(name = "T_ACCOUNT_REQUEST_STATUS")
#SequenceGenerator(
name = "SEQ_T_ACCOUNT_REQUEST_STATUS",
sequenceName = "SEQ_T_ACCOUNT_REQUEST_STATUS",
allocationSize = 1
)
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class AccountRequestStatus extends AbstractAuditingEntity {
private Long id;
private AccountRequestStatusEnum accountRequestStatusEnum;
private AccountRequest accountRequest;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_T_ACCOUNT_REQUEST_STATUS")
#Column(name = "ID", nullable = true, insertable = true, updatable = true, precision = 0)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Transient
public AccountRequestStatusEnum getAccountRequestStatusEnum() {
return accountRequestStatusEnum;
}
public void setAccountRequestStatusEnum(AccountRequestStatusEnum accountRequestStatusEnum) {
this.accountRequestStatusEnum = accountRequestStatusEnum;
}
#Column(name = "ACCOUNT_REQUEST_STATUS")
public Integer getAccountRequestStatusCode() {
if (Objects.nonNull(accountRequestStatusEnum)) {
return accountRequestStatusEnum.getType();
} else return null;
}
public void setAccountRequestStatusCode(Integer type) {
accountRequestStatusEnum = AccountRequestStatusEnum.of(type);
}
#ManyToOne(targetEntity = AccountRequest.class)
#JoinColumn(name = "ACCOUNT_REQUEST", referencedColumnName = "ID")
public AccountRequest getAccountRequest() {
return accountRequest;
}
public void setAccountRequest(AccountRequest accountRequest) {
this.accountRequest = accountRequest;
}
}
The first time that an account request comes from MQ my to application, I save the initial code of AccountRequestStatusEnum in service like below. This status persists properly and there is no problem, but when I want to update the AccountRequestStatus and add a new success code of AccountRequestStatusEnum (in another service) it won't be saved in DB.
This is the first service that is called after receiving the account request and saving the initial code.
#Service
#Transactional(readOnly = true)
public class AccountRequestServiceImpl implements IAccountRequestService {
#Value("${mq.event_argument_key}")
private String eventArgumentKey;
private final AccountRequestRepository accountRequestRepository;
private final AccountRequestStatusServiceImpl mqRequestStatusService;
private final EventToAccountRequestEntityMapper eventMapper;
private final AccountRequestMapper accountRequestMapper;
#Autowired
public AccountRequestServiceImpl(AccountRequestRepository accountRequestRepository,
AccountRequestStatusServiceImpl mqRequestStatusService,
EventToAccountRequestEntityMapper eventMapper,
AccountRequestMapper accountRequestMapper) {
this.accountRequestRepository = accountRequestRepository;
this.mqRequestStatusService = mqRequestStatusService;
this.eventMapper = eventMapper;
this.accountRequestMapper = accountRequestMapper;
}
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)// to prevent rollback for whole receive method in mq service
public void saveAccountRequest(Event event) {
AccountRequest accountRequest = eventMapper.eventToAccountRequest(event, eventArgumentKey);
accountRequestRepository.save(accountRequest);
AccountRequestDto accountRequestDto = accountRequestMapper.toDto(accountRequest);
saveAccountRequestStatus(accountRequestDto, AccountRequestStatusEnum.INITIAL);
}
private void saveAccountRequestStatus(AccountRequestDto accountRequestDto, AccountRequestStatusEnum status) {
AccountRequestStatusDto accountRequestStatusDto = new AccountRequestStatusDto();
accountRequestStatusDto.setAccountRequestStatusEnum(status);
accountRequestStatusDto.setAccountRequestDto(accountRequestDto);
mqRequestStatusService.saveAccountRequestStatus(accountRequestStatusDto);
}
}
This is the second service that should save the success code of AccountRequestStatus.
#Service
#Transactional(readOnly = true)
public class SyncLegacyAccountServiceImpl implements ISyncLegacyAccountService {
#Value("${mq.event_argument_key}")
private String eventArgumentKey;
#Value("${range.account_title_code}")
private String accountTitleCode;
private static final Logger log = LoggerFactory.getLogger(SyncLegacyAccountServiceImpl.class);
private final AccountMapRepository accountMapRepository;
private final AccountRequestRepository accountRequestRepository;
private final CustomerRepository customerRepository;
private final CustomerPersonRepository customerPersonRepository;
private final CustomerCompanyRepository customerCompanyRepository;
private final IMQService iMQService;
private final AccountRequestStatusServiceImpl accountRequestStatusServiceImpl;
private final GalaxyApi galaxyApi;
private final RangeApi rangeApi;
private final CustomerMapper customerMapper;
private final InquiryMapper inquiryMapper;
private final AccountRequestMapper accountRequestMapper;
private final EventToAccountRequestEntityMapper eventToAccountRequestMapper;
#Override
public void handleSyncRequest(Event event) {
saveSuccessfulAccountStatus(event); // ****** This is the main issue******
try {
CustomerAccountResponseDto galaxyData = getGalaxyData(event);
Optional<AccountMapEntity> optAccountMapEntity = accountMapRepository.findByNewAccountNo(event.getArgument().get(eventArgumentKey).toString());
if (!optAccountMapEntity.isPresent()) {
//openAccount(event);
} else {
AccountMapEntity accountMapEntity = optAccountMapEntity.get();
CustomerAccountResponseDto customerData = getCustomerData(accountMapEntity);
// save in legacy
}
} catch (Exception exception) {
handleEventRequestException(exception, event);
}
}
private void handleEventRequestException(Exception exception, Event event) {
if (exception instanceof RangeServiceException) {
log.error("Something went wrong with the Range service!");
throw new RangeServiceException();
} else if (exception instanceof GalaxySystemException) {
log.error("Something went wrong with the Galaxy service!");
NotifyAccountChangeResponse notifyAccountChangeResponse = MQUtil.buildAccountChangeResponse(new GalaxySystemException(), null, event.getMessageId());
iMQService.send(notifyAccountChangeResponse);
throw new GalaxySystemException();
}
}
public void saveSuccessfulAccountStatus(Event event) {
AccountRequest accountRequest = eventToAccountRequestMapper.eventToAccountRequest(event, eventArgumentKey);
AccountRequestDto accountRequestDto = accountRequestMapper.toDto(accountRequest);
saveAccountRequestStatus(accountRequestDto, AccountRequestStatusEnum.SUCCESS);
}
public void saveAccountRequestStatus(AccountRequestDto accountRequestDto, AccountRequestStatusEnum status) {
AccountRequestStatusDto accountRequestStatusDto = new AccountRequestStatusDto();
accountRequestStatusDto.setAccountRequestStatusEnum(status);
accountRequestStatusDto.setAccountRequestDto(accountRequestDto);
accountRequestStatusServiceImpl.saveAccountRequestStatus(accountRequestStatusDto);
}
}
UPDATE
AccountRequestStatusServiceImpl
#Service
#Transactional(readOnly = true)
public class AccountRequestStatusServiceImpl implements IAccountRequestStatusService {
private final AccountRequestStatusRepository accountRequestStatusRepository;
private final AccountRequestStatusMapper accountRequestStatusMapper;
#Autowired
public AccountRequestStatusServiceImpl(AccountRequestStatusRepository accountRequestStatusRepository,
AccountRequestStatusMapper accountRequestStatusMapper) {
this.accountRequestStatusRepository = accountRequestStatusRepository;
this.accountRequestStatusMapper = accountRequestStatusMapper;
}
#Override
#Transactional
public void saveAccountRequestStatus(AccountRequestStatusDto accountRequestStatusDto) {
AccountRequestStatus accountRequestStatus = accountRequestStatusMapper.toAccountRequestStatus(accountRequestStatusDto);
accountRequestStatusRepository.save(accountRequestStatus);
}
}
AccountRequestDto
#Data
public class AccountRequestDto {
private Long id;
private String messageId;
private String issuer;
private EventType type;
private EventName name;
private String accountNo;
private LocalDateTime dateTime;
}
AccountRequestStatusDto
#Data
public class AccountRequestStatusDto {
private Long id;
private AccountRequestStatusEnum accountRequestStatusEnum;
private AccountRequestDto accountRequestDto;
}
AccountRequestStatusMapper
#Mapper(componentModel = "spring")
public interface AccountRequestStatusMapper extends EntityToDtoMapper<AccountRequestStatusDto, AccountRequestStatus>, DtoToEntityMapper<AccountRequestStatusDto, AccountRequestStatus> {
#Mapping(target = "accountRequest.id", source = "accountRequestDto.id")
#Mapping(target = "accountRequest.messageId", source = "accountRequestDto.messageId")
#Mapping(target = "accountRequest.issuer", source = "accountRequestDto.issuer")
#Mapping(target = "accountRequest.type", source = "accountRequestDto.type")
#Mapping(target = "accountRequest.name", source = "accountRequestDto.name")
#Mapping(target = "accountRequest.accountNo", source = "accountRequestDto.accountNo")
#Mapping(target = "accountRequest.dateTime", source = "accountRequestDto.dateTime")
#Named(value = "toAccountRequestStatus")
AccountRequestStatus toAccountRequestStatus(AccountRequestStatusDto accountRequestStatusDto);
#Mapping(target = "accountRequestDto.id", source = "accountRequest.id")
#Mapping(target = "accountRequestDto.messageId", source = "accountRequest.messageId")
#Mapping(target = "accountRequestDto.issuer", source = "accountRequest.issuer")
#Mapping(target = "accountRequestDto.type", source = "accountRequest.type")
#Mapping(target = "accountRequestDto.name", source = "accountRequest.name")
#Mapping(target = "accountRequestDto.accountNo", source = "accountRequest.accountNo")
#Mapping(target = "accountRequestDto.dateTime", source = "accountRequest.dateTime")
#Named(value = "toAccountRequestStatusDto")
AccountRequestStatusDto toAccountRequestStatusDto(AccountRequestStatus accountRequestStatus);
}

You are not setting the id field of accountRequestStatus before saving. How is Hibernate supposed to know which entity to update? I'm guessing it is just generating a new id since you don't provide one. You need to somehow get the id of the already created accountRequestStatus and set it before updating. Or alternatively get the persisted accountRequestStatus entity and modify it.
That being said, I think you may be overcomplicating things here. Why does AccountRequestStatus need to be an entity? It does not seem to have an identity itself, it seems more like an attribute of AccountRequest. Can't you just add an AccountRequestStatusEnum field in AccountRequest, being after all a one-to-one relationship? If you are enclosing the status in a class because you foresee it may have more fields in the future, consider making it #Embeddable instead of an #Entity.
The general rule to know if a class must be an #Entity is that it must have an identity. The status of a request does not look like a good candidate, since its identity can simply be that of the request. Having the status be an #Entity means you now have an artificial id, which, surprise, is the root cause of your problem right now. If you want to better understand this topic, I recommend reading into the difference between #Entity and #Embeddable, and if you are feeling theoretical, read a bit about DDD(domain driven design).

Related

Incorrect auto-generated SQL query in Spring Boot (JPA) CrudRepository

My repository interfaces:
SongsRepository:
public interface SongRepository extends CrudRepository<Song, Integer> {
}
AlbumsRepository
public interface AlbumRepository extends CrudRepository<Album, Integer> {
}
My Model classes:
Album.java
#Entity
#Table(name = "albums", schema = "dbo")
public class Album {
#Id
#GeneratedValue
#Column(name = "album_id")
private Integer albumId;
#Column(name = "album_name")
private String albumName;
#Column(name = "released_by")
private String releasedBy;
#Column(name = "total_duration")
private Integer totalDuration;
#OneToMany(targetEntity = Song.class)
private List<Song> songs;
public Integer getAlbumId() {
return albumId;
}
public void setAlbumId(Integer albumId) {
this.albumId = albumId;
}
public String getAlbumName() {
return albumName;
}
public void setAlbumName(String albumName) {
this.albumName = albumName;
}
public String getReleasedBy() {
return releasedBy;
}
public void setReleasedBy(String releasedBy) {
this.releasedBy = releasedBy;
}
public Integer getTotalDuration() {
return totalDuration;
}
public void setTotalDuration(Integer totalDuration) {
this.totalDuration = totalDuration;
}
public List<Song> getSongs() {
return songs;
}
public void setSongs(List<Song> songs) {
this.songs = songs;
}
}
Song.java
#Entity
#Table(name = "songs", schema = "dbo")
public class Song {
#Id
#GeneratedValue
#Column(name = "song_id")
private Integer songId;
#Column(name = "song_name")
private String songName;
#Column(name = "album_id")
private Integer songAlbumId;
#Column(name = "song_duration")
private String songDuration;
#ManyToOne(targetEntity = Album.class)
private Album album;
public Integer getSongId() {
return songId;
}
public void setSongId(Integer songId) {
this.songId = songId;
}
public String getSongName() {
return songName;
}
public void setSongName(String songName) {
this.songName = songName;
}
public Integer getSongAlbumId() {
return songAlbumId;
}
public void setSongAlbumId(Integer songAlbumId) {
this.songAlbumId = songAlbumId;
}
public String getSongDuration() {
return songDuration;
}
public void setSongDuration(String songDuration) {
this.songDuration = songDuration;
}
public Album getAlbum() {
return album;
}
public void setAlbum(Album album) {
this.album = album;
}
}
My Controller:
SongsController.java
#RestController
#RequestMapping("/songs")
public class SongsController {
#Autowired
private SongRepository songs;
#Autowired
private AlbumRepository albums;
#Autowired
private Services services;
#GetMapping("/")
public List<SongViewModel> getAllSongs() {
List<SongViewModel> listOfAllSongs = new ArrayList<>();
songs.findAll().forEach(song -> listOfAllSongs.add(services.translateToViewModel(song)));
return listOfAllSongs;
}
}
Services class:
Services.java:
#Service
public class Services {
#Autowired
private SongRepository songs;
#Autowired
private AlbumRepository albums;
public SongViewModel translateToViewModel(Song song) {
SongViewModel model = new SongViewModel();
model.setSongId(song.getSongId());
model.setSongAlbumId(song.getSongAlbumId());
model.setSongName(song.getSongName());
model.setSongDuration(song.getSongDuration());
model.setSongAlbumName(albums.findById(song.getSongAlbumId()).get().getAlbumName());
return model;
}
public AlbumViewModel translateToViewModel(Album album) {
AlbumViewModel model = new AlbumViewModel();
return model;
}
}
My View Models:
SongViewModel:
public class SongViewModel {
private String songName;
private String songAlbumName;
private String songDuration;
private Integer songId;
private Integer songAlbumId;
public String getSongName() {
return songName;
}
public void setSongName(String songName) {
this.songName = songName;
}
public String getSongAlbumName() {
return songAlbumName;
}
public void setSongAlbumName(String songAlbumName) {
this.songAlbumName = songAlbumName;
}
public String getSongDuration() {
return songDuration;
}
public void setSongDuration(String songDuration) {
this.songDuration = songDuration;
}
public Integer getSongId() {
return songId;
}
public void setSongId(Integer songId) {
this.songId = songId;
}
public Integer getSongAlbumId() {
return songAlbumId;
}
public void setSongAlbumId(Integer songAlbumId) {
this.songAlbumId = songAlbumId;
}
}
Logged SQL query:
select song0_.song_id as song_id1_1_, song0_.album_album_id as album_al5_1_, song0_.album_id as album_id2_1_, song0_.song_duration as song_dur3_1_, song0_.song_name as song_nam4_1_ from dbo.songs song0_
Error:
ERROR: column song0_.album_album_id does not exist
My Database Schema (PostgreSQL 12.10.1)
This is my first time using Spring Boot and JPA. What have I done wrong here?
From the error message, I am guessing that it is trying to find a column called album_album_id which does not exist in my database and as a result is unable to find it; but why is it searching for that particular column? (My guess is I've messed up the relationship attributes).
Your mapping is wrong. Song should look like this:
#Entity
#Table(name = "songs", schema = "dbo")
public class Song {
#Id
#GeneratedValue
#Column(name = "song_id")
private Integer id;
#Column(name = "song_name")
private String name;
#Column(name = "album_id")
private Integer albumId;
#Column(name = "song_duration")
private String duration;
#ManyToOne
#JoinColumn(name = "album_id", insertable = false, updatable = false)
private Album album;
}
and Album like this:
#Entity
#Table(name = "albums", schema = "dbo")
public class Album {
#Id
#GeneratedValue
#Column(name = "album_id")
private Integer id;
#Column(name = "album_name")
private String name;
#Column(name = "released_by")
private String releasedBy;
#Column(name = "total_duration")
private Integer totalDuration;
#OneToMany(mappedBy = "album")
private List<Song> songs;
}
By the way,
it's a little strange to have song duration formatted as String and album duration as Integer.
if you're willing to go from CrudRepository to JpaRepository, you may want to use Spring data projection instead of manually recasting your song to SongViewModels
Here's a little example of interface projection for your question
Your Song entity class should be changed like follows.
#Entity
#Table(name = "songs", schema = "dbo")
public class Song {
#Id
#GeneratedValue
#Column(name = "song_id")
private Integer songId;
#Column(name = "song_name")
private String songName;
#Column(name = "song_duration")
private String songDuration;
#JoinColumn(name = "album_id", referencedColumnName = "album_id")
#ManyToOne
private Album album;
public Integer getSongId() {
return songId;
}
public void setSongId(Integer songId) {
this.songId = songId;
}
public String getSongName() {
return songName;
}
public void setSongName(String songName) {
this.songName = songName;
}
public Integer getSongAlbumId() {
return songAlbumId;
}
public void setSongAlbumId(Integer songAlbumId) {
this.songAlbumId = songAlbumId;
}
public String getSongDuration() {
return songDuration;
}
public void setSongDuration(String songDuration) {
this.songDuration = songDuration;
}
public Album getAlbum() {
return album;
}
public void setAlbum(Album album) {
this.album = album;
}
}
now you can get alubm_id by song enntity using song.getAlbum().getAlbumId()

Child object missing in Java Rest webservice

i am trying to call the get method of the below web service
#Stateless
#Path("com.company.entitiesms.bom")
public class BomFacadeREST extends AbstractFacade<Bom> {
#PersistenceContext(unitName = "com.mycompany_rxws_war_1.0-SNAPSHOTPU")
private EntityManager em;
public BomFacadeREST() {
super(Bom.class);
}
#GET
#Path("{id}")
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Bom find(#PathParam("id") Long id) {
return super.find(id);
}
where it should get all the xml elements and child elements in the entity class BOM:
#Entity
#Table(catalog = "MOI", schema = "dbo")
#XmlRootElement
public class Bom implements Serializable {
#Id
#Basic(optional = false)
#NotNull
#Column(name = "BOM_ID", nullable = false)
private Long bomId;
#OneToMany(mappedBy = "bomId")
private Collection<BOMDevices> bOMDevicesCollection;
#OneToMany(mappedBy = "bomId")
private Collection<TRInstallations> tRInstallationsCollection;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "bomId")
private Collection<BOMATEs> bOMATEsCollection;
public Bom() {
}
public Bom(Long bomId) {
this.bomId = bomId;
}
public Long getBomId() {
return bomId;
}
public void setBomId(Long bomId) {
this.bomId = bomId;
}
//#XmlTransient
public Collection<BOMATEs> getBOMATEsCollection() {
return bOMATEsCollection;
}
public void setBOMATEsCollection(Collection<BOMATEs> bOMATEsCollection) {
this.bOMATEsCollection = bOMATEsCollection;
}
#XmlTransient
public Collection<BOMDevices> getBOMDevicesCollection() {
return bOMDevicesCollection;
}
public void setBOMDevicesCollection(Collection<BOMDevices>
bOMDevicesCollection) {
this.bOMDevicesCollection = bOMDevicesCollection;
}
the problem is Collection of BOMATEs is not generated in the xml output of the get method in the above web service and its class is as follow:
#Entity
#Table(name = "BOM_ATEs", catalog = "MOI", schema = "dbo")
#XmlRootElement
public class BOMATEs implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "ATE_ID", nullable = false)
private Long ateId;
public BOMATEs() {
}
public BOMATEs(Long ateId) {
this.ateId = ateId;
}
public Long getAteId() {
return ateId;
}
public void setAteId(Long ateId) {
this.ateId = ateId;
}
#XmlTransient
public Collection<BOMDevices> getBOMDevicesCollection() {
return bOMDevicesCollection;
}
public void setBOMDevicesCollection(Collection<BOMDevices>
bOMDevicesCollection) {
this.bOMDevicesCollection = bOMDevicesCollection;
}
public Bom getBomId() {
return bomId;
}
public void setBomId(Bom bomId) {
this.bomId = bomId;
}
}
i tried removing the XmlTransient annotation for the getter getBOMATEsCollection , changing the fetch type to eager with no hope

NullPointerException when adding Employee in Hibernate

I have a Hibernate Entity "Intranet" which has this code:
// Imports removed
#Entity
#Table(name = "intranets", uniqueConstraints = #UniqueConstraint(columnNames = "company_name"))
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Intranet {
#Id
#Column(name = "INTRANET_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private int intranetId;
#Column(name = "owner_id")
private int ownerId;
#Column(name = "setup_done")
private boolean isSetUp = false;
#Column(name = "company_name")
private String companyName;
#ElementCollection
#CollectionTable(name = "employees")
#Column(name = "employee")
private List<Integer> employeeIds;
public int getIntranetId() {
return intranetId;
}
public void setIntranetId(int intranetId) {
this.intranetId = intranetId;
}
public boolean isSetUp() {
return isSetUp;
}
public void setSetUp(boolean isSetUp) {
this.isSetUp = isSetUp;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public int getOwnerId() {
return ownerId;
}
public void setOwnerId(int ownerId) {
this.ownerId = ownerId;
}
public List<Integer> getEmployeeIds() {
return employeeIds;
}
public void setEmployeeIds(List<Integer> employeeIds) {
this.employeeIds = employeeIds;
}
public void addEmployee(int id) {
this.employeeIds.add(id);
}
public void removeEmployee(int id) {
this.employeeIds.remove(new Integer(id));
}
}
As you can see, I added two methods that don't directly are getters and setters but they're like convenience methods to have easy access to the collection and adding/removing employees.
When I call "addEmployee()" in my program, I am getting a simple NullPointerException. The question basically is how I can initialize the collection to not have null because I read that Hibernate uses internal implementations of collections and does not simply takes ArrayList.
Thanks for help in advance.
You have a NullPointerException because you have to initialize the list yourself. It does not matter which implementation you use. This will fix your problem:
#ElementCollection
#Column(name = "employee")
private List<Integer> employeeIds = new ArrayList<>();

A cycle is detected in the object graph. This will cause infinitely deep XML

I have two DTO objects say A and B which are having getters and setters and are used to take data from the database. The problem is when I am calling A, B gets called and B again points itself to A and a cycle is created.
I cannot ignore/hide the method which is creating the cycle. I need to take the whole data of A and B.
Is there any way to achieve it ?
Please help
This is my code which is causing the problem. This is application DTO which is calling environment DTO
#OneToMany(mappedBy="application", fetch=FetchType.LAZY
,cascade=CascadeType.ALL
)
public Set<EnvironmentDTO> getEnvironment() {
return environment;
}
public void setEnvironment(Set<EnvironmentDTO> environment) {
this.environment = environment;
}
And this is environment DTO which is calling the application DTO
#ManyToOne(targetEntity=ApplicationDTO.class )
#JoinColumn(name="fk_application_Id")
public ApplicationDTO getApplication() {
return application;
}
public void setApplication(ApplicationDTO application) {
this.application = application;
}
Here cycle is getting created
This is my rest call which will give result in XML format and I think while creating XML cycle is getting created
#GET
#Path("/get")
#Produces({MediaType.APPLICATION_XML})
public List<ApplicationDTO> getAllApplications(){
List<ApplicationDTO> allApplication = applicationService.getAllApplication();
return allApplication;
}
This is the Application DTO class
#Entity
#Table(name="application")
#org.hibernate.annotations.GenericGenerator(
name ="test-increment-strategy",strategy = "increment")
#XmlRootElement
public class ApplicationDTO implements Serializable {
#XmlAttribute
public Long appTypeId;
private static final long serialVersionUID = -8027722210927935073L;
private Long applicationId;
private String applicationName;
private ApplicationTypeDTO applicationType;
private String applicationDescription;
private Integer owner;
private Integer createdBy;
private Integer assignedTo;
private Date createTime;
private Date modifiedTime;
private Set<EnvironmentDTO> environment;
#Id
#GeneratedValue(generator = "test-increment-strategy")
#Column(name = "applicationId")
public Long getApplicationId() {
return applicationId;
}
private void setApplicationId(Long applicationId) {
this.applicationId = applicationId;
}
#Column(name = "applicationName")
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
#ManyToOne(targetEntity=ApplicationTypeDTO.class
,fetch = FetchType.LAZY
)
#JoinColumn(name="applicationType")
public ApplicationTypeDTO getApplicationType() {
return applicationType;
}
public void setApplicationType(ApplicationTypeDTO applicationType) {
this.applicationType = applicationType;
}
#Column(name = "description")
public String getApplicationDescription() {
return applicationDescription;
}
public void setApplicationDescription(String applicationDescription) {
this.applicationDescription = applicationDescription;
}
#Column(name = "owner")
public Integer getOwner() {
return owner;
}
public void setOwner(Integer owner) {
this.owner = owner;
}
#Column(name = "createdBy")
public Integer getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Integer createdBy) {
this.createdBy = createdBy;
}
#Column(name = "assignedTo")
public Integer getAssignedTo() {
return assignedTo;
}
public void setAssignedTo(Integer assignedTo) {
this.assignedTo = assignedTo;
}
#Column(name = "createTime")
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
#Column(name = "modifiedTime")
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
#OneToMany(mappedBy="application", fetch=FetchType.LAZY
,cascade=CascadeType.ALL
)
public Set<EnvironmentDTO> getEnvironment() {
return environment;
}
public void setEnvironment(Set<EnvironmentDTO> environment) {
this.environment = environment;
}
This is the Environment DTO class
#Entity
#Table(name="environment")
#org.hibernate.annotations.GenericGenerator(
name = "test-increment-strategy",
strategy = "increment")
#XmlRootElement
public class EnvironmentDTO implements Serializable {
#XmlAttribute
public Long envTypeId;
#XmlAttribute
public Long appId;
private static final long serialVersionUID = -2756426996796369998L;
private Long environmentId;
private String environmentName;
private EnvironmentTypeDTO environmentType;
private Integer owner;
private Date createTime;
private Set<InstanceDTO> instances;
private ApplicationDTO application;
#Id
#GeneratedValue(generator = "test-increment-strategy")
#Column(name = "envId")
public Long getEnvironmentId() {
return environmentId;
}
private void setEnvironmentId(Long environmentId) {
this.environmentId = environmentId;
}
#Column(name = "envName")
public String getEnvironmentName() {
return environmentName;
}
public void setEnvironmentName(String environmentName) {
this.environmentName = environmentName;
}
#ManyToOne(targetEntity=EnvironmentTypeDTO.class)
#JoinColumn(name = "envType")
public EnvironmentTypeDTO getEnvironmentType() {
return environmentType;
}
public void setEnvironmentType(EnvironmentTypeDTO environmentType) {
this.environmentType = environmentType;
}
#Column(name = "owner")
public Integer getOwner() {
return owner;
}
public void setOwner(Integer owner) {
this.owner = owner;
}
#Temporal(TemporalType.DATE)
#Column(name = "createTime")
public Date getCreateTime()
{
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
#OneToMany(mappedBy="environment", cascade=CascadeType.ALL, fetch = FetchType.EAGER)
public Set<InstanceDTO> getInstances() {
return instances;
}
public void setInstances(Set<InstanceDTO> instances) {
this.instances = instances;
}
#ManyToOne(targetEntity=ApplicationDTO.class )
#JoinColumn(name="fk_application_Id")
//#XmlTransient
public ApplicationDTO getApplication() {
return application;
}
public void setApplication(ApplicationDTO application) {
this.application = application;
}
Your object graph is cyclic. There is nothing intrinsically wrong with that, and it is a natural consequence of using JPA.
Your problem is not that your object graph is cyclic, but that you are encoding it in a format which cannot handle cycles. This isn't a Hibernate question, it's a JAXB question.
My suggestion would be to stop JAXB from attempting to marshal the application property of the EnvironmentDTO class. Without that property the cyclic graph becomes a tree. You can do this by annotating that property with #XmlTransient.
(confession: i learned about this annotation by reading a blog post by Mr Doughan, which i came across after reading his answer to this question!)
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
MOXy offers the #XmlInverseReference extension to handle this use case. Below is an example of how to apply this mapping on two entities with a bidirectional relationship.
Customer
import javax.persistence.*;
#Entity
public class Customer {
#Id
private long id;
#OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
Address
import javax.persistence.*;
import org.eclipse.persistence.oxm.annotations.*;
#Entity
public class Address implements Serializable {
#Id
private long id;
#OneToOne
#JoinColumn(name="ID")
#MapsId
#XmlInverseReference(mappedBy="address")
private Customer customer;
}
For More Information
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://blog.bdoughan.com/2013/03/moxys-xmlinversereference-is-now-truly.html
My advice is not exposing your JPA entity class to your webservices. You can create different POJO class and convert your JPA entity to the POJO. For example:
this is your JPA entity
import javax.persistence.*;
#Entity
public class Customer {
#Id
private long id;
#OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
you should use this class for your webservices:
public class CustomerModel{
private long id;
//you can call different WS to get the Address class, or combine to this model
public void setFromJpa(Customer customer){
this.id = customer.id;
}
}

dataaccess error--> getInt not implemented for class oracle.jdbc.driver.T4CDateAccessor

I have recently setup a spring + hibernate project. I am using oracle DB. I have a entity as shown in the code.
#Entity
#Table(name = "P_EMP_STATUS")
public class EmployeeStatus extends AbstractEntity<Integer> implements Serializable{
private static final long serialVersionUID = 5451825528280340412L;
private Integer id;
private Region region;
private Project project;
private TaskType taskName;
private String taskType
private PrinceUser princeUser;
private Integer assignee;
private Date actualStartDate;
private Date actualFinishDate;
private Integer scheduledStartDate;
private Integer scheduledFinishDate;
private Integer effortSpent;
private String empComments;
private String mgrcomments;
private String archive;
#Id
#Column(name = "ID")
#GeneratedValue(generator="P_STATUS_SEQ", strategy=GenerationType.AUTO)
#SequenceGenerator(name="P_STATUS_SEQ", sequenceName="P_STATUS_SEQ", allocationSize=1)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "REGION_ID")
public Region getRegion() {
return region;
}
public void setRegion(Region region) {
this.region = region;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "PROJECT_ID")
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "TASK_ID")
public TaskType getTaskName() {
return taskName;
}
public void setTaskName(TaskType taskName) {
this.taskName = taskName;
}
#Column(name = "TASK_TYPE")
public String getTaskType() {
return taskType;
}
public void setTaskType(String taskType) {
this.taskType = taskType;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "ASSIGNEE")
public PrinceUser getPrinceUser() {
return princeUser;
}
public void setPrinceUser(PrinceUser princeUser) {
this.princeUser = princeUser;
}
#Column(name = "ACT_START")
public Date getActualStartDate() {
return actualStartDate;
}
public void setActualStartDate(Date actualStartDate) {
this.actualStartDate = actualStartDate;
}
#Column(name = "ACT_FINISH")
public Date getActualFinishDate() {
return actualFinishDate;
}
public void setActualFinishDate(Date actualFinishDate) {
this.actualFinishDate = actualFinishDate;
}
#Column(name = "SCH_START")
public Integer getScheduledStartDate() {
return scheduledStartDate;
}
public void setScheduledStartDate(Integer scheduledStartDate) {
this.scheduledStartDate = scheduledStartDate;
}
#Column(name = "SCH_FINISH")
public Integer getScheduledFinishDate() {
return scheduledFinishDate;
}
public void setScheduledFinishDate(Integer scheduledFinishDate) {
this.scheduledFinishDate = scheduledFinishDate;
}
#Column(name = "EFFORT_SPENT")
public Integer getEffortSpent() {
return effortSpent;
}
public void setEffortSpent(Integer effortSpent) {
this.effortSpent = effortSpent;
}
#Column(name = "EMP_COMMENTS")
public String getEmpComments() {
return empComments;
}
public void setEmpComments(String empComments) {
this.empComments = empComments;
}
#Column(name = "MGR_COMMENTS")
public String getMgrcomments() {
return mgrcomments;
}
public void setMgrcomments(String mgrcomments) {
this.mgrcomments = mgrcomments;
}
#Column(name = "ARCHIVE")
public String getArchive() {
return archive;
}
public void setArchive(String archive) {
this.archive = archive;
}
When i try to get data from DB using
Query query = em.createQuery("FROM EmployeeStatus");
return query.getResultList();
I get the following error.
java.sql.SQLException: Invalid column type: getInt not implemented for class oracle.jdbc.driver.T4CDateAccessor
oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)
oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:146)
oracle.jdbc.driver.Accessor.unimpl(Accessor.java:358)
I have checked the mappings, everything seems alright. Can someone please help me?
You declared two dates as Integer properties:
private Integer scheduledStartDate;
private Integer scheduledFinishDate;
These fields are probably stored in a column of type Date in database, and the database driver doesn't know how to convert a date to an integer.

Categories