Java JPA OneToMany relation and query - java

maybe duplicate question but I couldn't fina a solution for my case which I think is pretty simple.
I have two tables like so :
And those are the related DTO Object :
First table
#Entity
#Table(name = "DA10003_REF_SIGNALEMENT")
public class RefSignalement {
#Id
#Column(name = "CD_SIGNALEMENT")
public String codeSignalement;
#Column(name = "LIBELLE")
public String libelle;
#Column(name = "CATEGORIE")
public String categorie;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "CD_SIGNALEMENT")
public List<RefMessage> refMessages;
}
Second table :
#Entity
#Table(name = "DA10004_REF_MESSAGE")
public class RefMessage {
#Id
#Column(name = "CD_SIGNALEMENT")
public String codeSignalement;
#Id
#Column(name = "DESTINATAIRE")
public String destinataires;
#Column(name = "MESSAGE")
public String message;
}
And the following query to get all the RefSignelement with the associated message :
List<RefSignalement> listRefSignalement = em.createQuery("SELECT p FROM RefSignalement p, RefMessage m", RefSignalement.class).getResultList();
Unfortunately it's returning an empty list, I have tried to change it with join fetch but nothing change.
Thank for the help

Remember that in JPQL you have to think in Objects, not relations. You want to fetch all 'RefSignalement' and eagerly fetch their 'refMessages' properties:
SELECT DISTINCT s FROM RefSignalement s JOIN FETCH s.refMessages
Here the "distinct" is only needed by JPA when assembling your resulting entities, but add unnecessary overhead to the SQL Query. If you have a Hibernate version >= 5.2.2 (I think), then there is a query hint you can use to avoid that:
List<RefSignalement> sigs = entityManager
.createQuery(
"select distinct s " +
"from RefSignalement s " +
"left join fetch s.refMessages ")
.setHint("hibernate.query.passDistinctThrough", false)
.getResultList();
Read more about it here.

a couple of things, RefMessage class is using composite primary key so i guess you need to use #IdClass or #EmbeddedId annotation. here I'm providing using
#IdClass
public class RefId implements Serializable {
private String codeSignalement;
private String destinataires;
// default constructor
public RefId() {
}
public RefId(String codeSignalement, String destinataires) {
this.codeSignalement = codeSignalement;
this.destinataires = destinataires;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RefId refId = (RefId) o;
return Objects.equals(codeSignalement, refId.codeSignalement) &&
Objects.equals(destinataires, refId.destinataires);
}
#Override
public int hashCode() {
return Objects.hash(codeSignalement, destinataires);
}
}
then you need to use like follows
#Entity
#Table(name = "DA10004_REF_MESSAGE")
#IdClass(RefId.class)
public class RefMessage {
#Id
#Column(name = "CD_SIGNALEMENT")
public String codeSignalement;
#Id
#Column(name = "DESTINATAIRE")
public String destinataires;
#Column(name = "MESSAGE")
public String message;
}
define your repository as follows:
public interface RefSignalementRepo extends
JpaRepository<RefSignalement, String> {
}
RefSignalement class defination as follows:
#Entity
#Table(name = "DA10003_REF_SIGNALEMENT")
public class RefSignalement {
#Id
#Column(name = "CD_SIGNALEMENT")
public String codeSignalement;
#Column(name = "LIBELLE")
public String libelle;
#Column(name = "CATEGORIE")
public String categorie;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
#JoinColumn(name = "CD_SIGNALEMENT")
public List<RefMessage> refMessages;
}
very example app
#SpringBootApplication
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(App.class, args);
RefSignalementRepo repo = context.getBean(RefSignalementRepo.class);
RefSignalement obj = new RefSignalement();
obj.codeSignalement = "1";
obj = repo.save(obj);
obj.refMessages = new ArrayList<>();
RefMessage message = new RefMessage();
message.codeSignalement = "1";
message.destinataires = "2";
message.message = "custom message";
obj.refMessages.add(message);
obj = repo.save(obj);
List<RefSignalement> objs = repo.findAll();
System.out.println(objs.get(0).refMessages.size());
EntityManager em = context.getBean(EntityManager.class);
List<RefSignalement> listRefSignalement = em.createQuery("SELECT p FROM RefSignalement p, RefMessage m", RefSignalement.class).getResultList();
System.out.println(listRefSignalement.get(0).refMessages.size());
}
}

Related

Hibernate: Entity property from non-entity class

so I have an hibernate Entity called Appointment, in this entity I have a AppointNumber property which itself contains a number property which is a string.
When I persist my Appointment, I need the AppointmentNumber. I got it to work with #Embedded and #Embeddable the other day but this creates a join table Which I can't have.
I tried many other solutions to try and get it to work without join tables but I can't figure it out. (I get lots of ava.lang.IllegalStateException)
Can anyone help?
Thanks!
#Entity(name = "appointments")
public class Appointment {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#OneToOne(mappedBy = "number")
#Fetch(value = FetchMode.SELECT)
private AppointmentNumber appointmentNumber;
Appointment entity
AppointmentNumber, used in Appointment but should not be an entity
public class AppointmentNumber {
#OneToOne
#JoinColumn(name = "appointmentNumber", unique = true, nullable = false)
private String number;
You could do like this:
#Entity(name = "appointments")
public class Appointment {
///....
#Convert(converter = AppointmentNumberConverter.class)
private AppointmentNumber appointmentNumber;
///....
}
#Converter
public class AppointmentNumberConverter implements
AttributeConverter<PersonName, String> {
#Override
public String convertToDatabaseColumn(AppointmentNumber appointmentNumber) {
if (appointmentNumber == null) {
return null;
}
return appointmentNumber.getNumber();
}
#Override
public AppointmentNumber convertToEntityAttribute(String appointmentNumber) {
if (appointmentNumber == null) {
return null;
}
AppointmentNumber result = new AppointmentNumber();
result.setNumber(appointmentNumber);
return result;
}
}
Have a look at JPA Converter documentation.

Hibernate persists only first nested entity

My problem is that Hibernate does not persist nested entities given in entity.
Consider following entities:
PollEntity
#Table(name = "\"poll\"")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#EqualsAndHashCode(of = "id")
#Builder
public class PollEntity {
#Transient
public OptionEntity addOption(OptionEntity pollOptionEntity) {
if(options == null)
options = new HashSet<>();
options.add(pollOptionEntity);
pollOptionEntity.setPoll(this);
return pollOptionEntity;
}
#Transient
public OptionEntity dropOption(OptionEntity pollOptionEntity) {
options.remove(pollOptionEntity);
pollOptionEntity.setPoll(null);
return pollOptionEntity;
}
#Id
#Column(name = "id")
private UUID id;
#Column(name = "author")
#UUIDv4
private UUID author;
#Column(name = "poll_question")
#Size(max = 1000)
private String pollQuestion;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "poll", cascade = CascadeType.MERGE)
#Builder.Default
#Valid
private Set<OptionEntity> options;
}
OptionEntity
#Table(name = "\"option\"")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#EqualsAndHashCode(of = "id")
#Builder
public class OptionEntity {
#Id
#GeneratedValue(generator = "UUID")
#Column(name = "id")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#JoinColumn(name = "poll_id")
#ManyToOne
private PollEntity poll;
#Column(name = "option")
#Size(max = 1000)
#NotNull
private String option;
}
And here's service method:
#Transactional(rollbackFor = Throwable.class)
public void createPoll(#Valid PollEntity pollEntity) throws ValidationException {
validationService.validateOrThrow(pollEntity);
if (pollRepository.findById(pollEntity.getId()).isPresent())
throw new ValidationException("Invalid id", Map.of("id", "Poll with id (" + pollEntity.getId() + ") already exists"));
pollEntity = validationService.validateAndSave(pollRepository, pollEntity);
And corresponding test:
#Test
public void createPollTest() throws ValidationException {
var uuid = UUID.randomUUID();
var pollOption1 = OptionEntity.builder()
.option("Test option 1")
.build();
var pollOption2 = OptionEntity.builder()
.option("Test option 2")
.build();
var pollOption3 = OptionEntity.builder()
.option("Test option 3")
.build();
var poll = PollEntity.builder()
.id(uuid)
.pollQuestion("Test question")
.author(UUID.randomUUID())
.build();
poll.addOption(pollOption1);
poll.addOption(pollOption2);
poll.addOption(pollOption3);
pollService.createPoll(poll);
}
Which gives following output in database
poll
2e565f50-7cd4-4fc9-98cd-49e0f0964487 feae5781-ff07-4a21-9292-c11c4f1a047d Test question
option
c786fe5d-632d-4e94-95ef-26ab2af633e7 fc712242-8e87-41d8-93f2-ff0931020a4a Test option 1
and rest options ended up unpersisted.
I've also used to create options in separate method
#Transactional(propagation= Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
public Set<OptionEntity> createOptions(#Valid Set<OptionEntity> pollOptionsEntities) throws ValidationException {
for (var pollOption : pollOptionsEntities) {
validationService.validateAndSave(pollOptionsRepository, pollOption);
}
return pollOptionsEntities;
}
and option entities were getting produced but had to switch to persisting from built-in entity methods due to errors with persisting poll entity.
Database schema looks like this:
CREATE TABLE "poll"
(
"id" UUID PRIMARY KEY,
"author" UUID NOT NULL,
"poll_question" VARCHAR(1000) NOT NULL
)
CREATE TABLE "option"
(
"id" UUID PRIMARY KEY,
"poll_id" UUID NOT NULL REFERENCES "poll" ("id") ON DELETE CASCADE
"option" VARCHAR(1000) NOT NULL
)
What are the possible approaches to try?
UPD 1
Having considered various looking-alike questions (1,2,3,4,5)
I've came up with this addition to entity which suppose make entities persistence regardless of actual value and still having only one option in output. What was done wrong?
#Override
public boolean equals(Object object) {
if ( object == this ) {
return false;
}
if ( object == null || object.getClass() != getClass() ) {
return false;
}
final OptionEntity other = OptionEntity.class.cast( object );
if ( getId() == null && other.getId() == null ) {
return false;
}
else {
return false;
}
}
#Override
public int hashCode() {
final HashCodeBuilder hcb = new HashCodeBuilder( 17, 37 );
if ( id == null ) {
while (getOptions().iterator().hasNext())
hcb.append( getOptions().iterator().next() );
}
else {
hcb.append( id );
}
hcb.append( options );
return hcb.toHashCode();
}
So the answer were quite trivial all the way long.
In order to overcome this the only thing that should be done is to change container: Set<OptionEntity> to List<OptionEntity>.
Hope this will not produce some hard-to-tackle bugs but if it can - please add comment.
Because in my case uniqueness was not strict requirement, ended up with this:
PollEntity:
#Table(name = "\"poll\"")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Builder
public class PollEntity {
#Transient
public OptionEntity addOption(OptionEntity pollOptionEntity) {
options.add(pollOptionEntity);
pollOptionEntity.setPoll(this);
return pollOptionEntity;
}
#Transient
public OptionEntity dropOption(OptionEntity pollOptionEntity) {
options.remove(pollOptionEntity);
pollOptionEntity.setPoll(null);
return pollOptionEntity;
}
#Id
#Column(name = "id")
private UUID id;
#Column(name = "status")
#Enumerated(EnumType.STRING)
#NotNull
private Status status;
#Column(name = "author")
#UUIDv4
private UUID author;
#Column(name = "poll_question")
#Size(max = 1000)
private String pollQuestion;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "poll", cascade = CascadeType.MERGE)
#Builder.Default
#Valid
private List<OptionEntity> options = new ArrayList<>();
}

Spring rest Jpa query returning Null value

I am building a rest api on Jhipster that must return the billers details using a Category ID as the search parameter. The call to the categories endpoint is returning list of categories but the call to the billers endpoint using one of the category id is returning a null result .
public interface ApplicationUrl {
String BILLERS = "/category/{categoryid}";
}
This is the controller :
#RequestMapping(ApplicationUrl.BASE_CONTEXT_URL)
public class BillingGatewayController {
#Autowired
private BillingGatewayService billingGatewayService;
#GetMapping(ApplicationUrl.BILLERS)
public BillersServiceResponse getAllBillersByCatId(#PathVariable Long categoryId) {
BillersServiceResponse defaultServiceResponse = new BillersServiceResponse();
defaultServiceResponse.setMessage(Message.fail.name());
ResponseCode responseCode = ResponseCode.BILLER_NOT_AVAILABLE;
log.debug("REST request to get all billers");
List<BillerDto> billers = billingGatewayService.findBillers(categoryId);
if (CollectionUtils.size(billers) > 0) {
responseCode = ResponseCode.SUCCESSFUL;
defaultServiceResponse.setStatus(responseCode.getCode());
defaultServiceResponse.setMessage(Message.SUCCESSFUL.name());
defaultServiceResponse.setData(billers);
}
defaultServiceResponse.setStatus(responseCode.getCode());
return defaultServiceResponse;
}
}
This is the service classes :
public interface BillingGatewayService {
List<BillerDto> findBillers(Long id);
}
public interface BillersRepository extends JpaRepository<Billers, Long>, JpaSpecificationExecutor<Billers> {
}
#Service("billingGatewayService")
public class BillingGatewayServiceImpl implements BillingGatewayService {
#Autowired
private ExtBillersRepository billersRepository;
#Override
public List<BillerDto> findBillers(Long categoryId) {
BillerResponseDto billerResponseDto = new BillerResponseDto();
List<BillerDto> billers = billersRepository.findAllActiveBillers(categoryId);
billerResponseDto.setBillers(billers);
billerResponseDto.setCategoryId(String.valueOf(categoryId));
return billers;
}
}
import com.fms.payfuze.dto.BillerDto;
import com.fms.payfuze.repository.BillersRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface ExtBillersRepository extends BillersRepository {
String ACTIVE_BILLERS = "select new com.fms.payfuze.dto.BillerDto(b.id, b.name) from Billers b inner join b.categories c where c.id=b.id order by b.name";
#Query(ACTIVE_BILLERS)
List<BillerDto> findAllActiveBillers(#Param("id") Long id);
}
This is the billerDTO :
public class BillerDto {
private String billerId;
private String nameOfBiller;
public BillerDto(Long id, String name) {
this.billerId = String.valueOf(id);
this.nameOfBiller = name;
}
public String getBillerId() {
return billerId;
}
public void setBillerId(String billerId) {
this.billerId = billerId;
}
public String getNameOfBiller() {
return nameOfBiller;
}
public void setNameOfBiller(String nameOfBiller) {
this.nameOfBiller = nameOfBiller;
}
}
and this is the Billers class:
#Entity
#Table(name = "billers")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Billers implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "active")
private Boolean active;
#Column(name = "date_created")
private Instant dateCreated;
#OneToOne
#JoinColumn(unique = true)
private BillersRouterConfig billersRouterConfig;
#OneToMany(mappedBy = "billers")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<TransactionDetails> billers = new HashSet<>();
#ManyToMany(mappedBy = "billers")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JsonIgnore
private Set<Categories> categories = new HashSet<>();
Getters and setters
I have been on it for days and been brain storming , I'll appreciate all inputs but constructive and reconstructive . Thanks
You're passing #Param("id") in your repository but you are not actually using that id in your SQL Query. Your where condition only has a join statement. You need to add AND c.id = :id after joining the table so you can mention which category you want to get by category id.
Also, it should be WHERE c.biller_id = b.id in your JOIN statement?
Try something like this
String ACTIVE_BILLERS =
"select new com.fms.payfuze.dto.BillerDto(b.id, b.name)
from Billers b inner join
b.categories c
where c.billers_id = b.id and c.billers_id = :id
order by b.name";

Criteria API Unable to locate appropriate constructor on class

When I am implementing the Criteria API join for my spring boot study, I tried joining 2 classes and fetching the result. But when I am implementing and running I am getting the following error,
Unable to locate appropriate constructor on class [com.spacestudy.model.Investigator]. Expected arguments are: com.spacestudy.model.Employee
[cause=org.hibernate.PropertyNotFoundException: no appropriate constructor in class: com.spacestudy.model.Investigator]
And my Employee.java class like the following,
#Entity
#Table(name="employee")
public class Employee implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "employee_seq_generator")
#SequenceGenerator(name = "employee_seq_generator", sequenceName = "employee_seq",allocationSize=1)
#Column(name="nemp_id",columnDefinition="serial")
public Integer nEmpId;
#Column(name="semp_name")
public String sEmpName;
#Column(name="sdesignation")
public String sDesignation;
#Column(name="ninst_id")
public Integer nInstId;
#Column(name="ndept_id")
public Integer nDeptId;
#Column(name="sclient_emp_id")
public String sClientEmpId;
#Column(name="ntemp_emp_id")
public Integer nTempEmpId;
#Column(name="bis_paid")
public boolean bIsPaid=true;
#Column(name="sunpaid_comment")
public String sUnpaidComment;
#ManyToOne(optional=true)
#JoinColumn(name="ndept_id", insertable = false, updatable = false)
public Department department;
#OneToMany(cascade = CascadeType.ALL,mappedBy="nEmpId")
public Set<Investigator> employeeInvestigatorJoinMapping;
public Employee() {
}
public Employee(Integer nEmpId, String sEmpName, String sDesignation, Integer nInstId, Integer nDeptId,
String sClientEmpId, Integer nTempEmpId, boolean bIsPaid, String sUnpaidComment, Department department,
Set<Investigator> employeeInvestigatorJoinMapping) {
super();
this.nEmpId = nEmpId;
this.sEmpName = sEmpName;
this.sDesignation = sDesignation;
this.nInstId = nInstId;
this.nDeptId = nDeptId;
this.sClientEmpId = sClientEmpId;
this.nTempEmpId = nTempEmpId;
this.bIsPaid = bIsPaid;
this.sUnpaidComment = sUnpaidComment;
this.department = department;
this.employeeInvestigatorJoinMapping = employeeInvestigatorJoinMapping;
}
}
And my second class Investigator.java,
#Entity
#Table(name = "investigator")
#JsonInclude(JsonInclude.Include.NON_NULL) // avoiding null values
public class Investigator implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "investigator_seq_generator")
#SequenceGenerator(name = "investigator_seq_generator", sequenceName = "investigator_seq")
#Column(name="ninvestigator_id")
public Integer nInvestigatorId;
#Column(name="sinvestigator_name")
public String sInvestigatorName;
#Column(name="ninst_id")
public Integer nInstId;
#Column(name="stitle")
public String sTitle;
#Column(name="ntemp_investigator_id")
public Integer nTempInvestigatorId;
#ManyToOne(optional = false)
#JoinColumn(name="nemp_id",referencedColumnName="nemp_id")
public Employee nEmpId;
// Default Constructor.
public Investigator()
{
}
public Investigator(Integer nInvestigatorId, String sInvestigatorName, Integer nInstId, String sTitle,
Integer nTempInvestigatorId, Employee nEmpId) {
super();
this.nInvestigatorId = nInvestigatorId;
this.sInvestigatorName = sInvestigatorName;
this.nInstId = nInstId;
this.sTitle = sTitle;
this.nTempInvestigatorId = nTempInvestigatorId;
this.nEmpId = nEmpId;
}
}
And Implemented the Criteria API joining like the following,
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Investigator> cq = cb.createQuery(Investigator.class);
Root<Employee> rootInvestigator = cq.from(Employee.class);
Join<Employee ,Investigator> resultEmployeeMappingObj
= rootInvestigator.join("employeeInvestigatorJoinMapping");
cq.multiselect(rootInvestigator);
cq.where(cb.equal(resultEmployeeMappingObj.get("nEmpId"), 21638));
List<Investigator> results = em.createQuery(cq).getResultList();
return results;
Where did I go wrong?
Criteria API
You have a few mistakes in the Criteria API query.
The working one looks like this
#Transactional(readOnly = true)
public List<Investigator> findByEmployeeId(int employeeId) {
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Investigator> query = criteriaBuilder.createQuery(Investigator.class);
Root<Investigator> investigator = query.from(Investigator.class);
Join<Investigator, Employee> employees = investigator.join("nEmpId");
query.select(investigator)
.where(criteriaBuilder.equal(employees.get("nEmpId"), employeeId));
TypedQuery<Investigator> typedQuery = em.createQuery(query);
List<Investigator> investigators = typedQuery.getResultList();
log.debug("Investigators: {}", investigators);
return investigators;
}
Spring Data JPA
Also, if your application is based on Spring Framework after renaming a few fields you can use Spring Data JPA and do not write query at all.
Employee entity:
#Entity
#Table(name = "employee")
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "employee_seq_generator")
#SequenceGenerator(name = "employee_seq_generator", sequenceName = "employee_seq", allocationSize = 1)
#Column(name = "nemp_id", columnDefinition = "serial")
public Integer id;
//...
#OneToMany(cascade = CascadeType.ALL, mappedBy = "employee")
public Set<Investigator> investigators = new HashSet<>();
//...
}
Investigator entity:
#Entity
#Table(name = "investigator")
#JsonInclude(JsonInclude.Include.NON_NULL) // avoiding null values
public class Investigator implements Serializable {
//...
#ManyToOne(optional = false)
#JoinColumn(name = "nemp_id", referencedColumnName = "nemp_id")
public Employee employee;
//...
}
Spring Data JPA repository interface:
public interface InvestigatorRepository extends JpaRepository<Investigator, Integer> {
List<Investigator> findByEmployeeId(int employeeId);
}
That's it. Now you can simply inject the repository and use it:
#Autowired
private InvestigatorRepository investigatorRepository;
public void testQuery() {
investigatorRepository.findByEmployeeId(employee.getId()));
}
The exception seems to me that Criteria likes to have an Investigator constructor that takes an Employee argument:
public Investigator(Employee nEmpId) {
super();
this.nEmpId = nEmpId;
}

Jpa/Hibernate Composite Key with many to one and #IdClass detached entity

I have two entities Train and Station. They have #OneToMany relation on Timetable entity. In Timetable entity I have composite key on Train and Station as #ManyToOne.
All classes have setters and getters
Train
#Entity
#Table(name = "train")
public class Train {
#Id
#GeneratedValue
private Long id;
#Column(name = "type")
private String type;
#OneToMany(mappedBy = "train",fetch = FetchType.EAGER)
#OrderBy("curentnumber ASC")
private List<Coach> coaches;
#OneToMany(mappedBy = "train",fetch = FetchType.LAZY)
#OrderBy("arrivalTime ASC")
private List<Timetable> timetable ;
...
}
Station
#Entity
#Table(name = "station")
public class Station {
#Id
#GeneratedValue
private Long id;
#Column(name = "name", length = 16, nullable = false)
private String name;
#OneToMany(mappedBy = "station", fetch = FetchType.LAZY)
private List<Timetable> timetable;
...
}
Timetable
#Entity
#IdClass(TimetableId.class)
#Table(name = "timetable")
public class Timetable implements Serializable, Comparable<Timetable>{
#Id
#ManyToOne
#JoinColumn(name = "train_id", referencedColumnName = "id")
private Train train;
#Id
#ManyToOne
#JoinColumn(name = "station_id", referencedColumnName = "id")
private Station station;
#Column(name = "arrivalTime")
private Timestamp arrivalTime;
#Column(name = "departureTime")
private Timestamp departureTime;
...
}
TimetableId
public class TimetableId implements Serializable{
Long train;
Long station;
public TimetableId() {
}
...
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TimetableId that = (TimetableId) o;
if (train != null ? !train.equals(that.train) : that.train != null) return false;
return station != null ? station.equals(that.station) : that.station == null;
}
#Override
public int hashCode() {
int result = train.hashCode();
result = 31 * result + station.hashCode();
return result;
}
}
I have service class too.
//Result timetable
Timetable addedTimetable = new Timetable();
addedTimetable.setTrain(new Train());
addedTimetable.getTrain().setId(Long.valueOf(trainid));
addedTimetable.setStation(new Station());
addedTimetable.getStation().setId(Long.valueOf(stationid));
try{
timetableRepository.create(addedTimetable);
} catch (Exception e) {
e.printStackTrace();
}
And some from GenericDAO
#Override
public void create(T entity) {
EntityManager manager = emf.createEntityManager();
try {
try {
manager.getTransaction().begin();
manager.persist(entity);
manager.getTransaction().commit();
}
finally {
if (manager.getTransaction().isActive())
manager.getTransaction().rollback();
}
} finally {
manager.close();
}
}
The problem is when I have #IdClass annotation and try to create new Timetable referenced on new Station() and new Train with existing id, then i get error for "detached entity passed to persist:entity.Station" in my dao level. But if i remove #IdClass annotation, then everything is good and Timetable creates correctly. I think that i make something wrong in TimetableId and annotations...
Please help me, i stack here.

Categories