Mapping a table with a composite primary key - java

I have the following Entity in hibernate, using JPA annotations
#Entity
#IdClass(PurchaseCounter.PurchaseCounterPK.class)
#Table(name = "customer_purchases_counter")
public class PurchaseCounter {
public static class PurchaseCounterPK implements Serializable {
Integer customerId;
Integer purchaseId;
public PurchaseCounterPK(Integer customerId, Integer purchaseId) {
this.customerId = customerId;
this.purchaseId = purchaseId;
}
public Integer getCustomerId() {
return customerId;
}
public void setCustomerId(Integer customerId) {
this.customerId = customerId;
}
public Integer getPurchaseId() {
return purchaseId;
}
public void setPurchaseId(Integer purchaseId) {
this.purchaseId = purchaseId;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PurchaseCounterPK that = (PurchaseCounterPK) o;
if (customerId != null ? !customerId.equals(that.customerId) : that.customerId != null) return false;
if (purchaseId != null ? !purchaseId.equals(that.purchaseId) : that.purchaseId != null) return false;
return true;
}
#Override
public int hashCode() {
int result = customerId != null ? customerId.hashCode() : 0;
result = 31 * result + (purchaseId != null ? purchaseId.hashCode() : 0);
return result;
}
}
Integer customerId;
Integer purchaseId;
Integer count = 0;
#Id
#Column(name = "customer_id")
public Integer getCustomerId() {
return customerId;
}
public void setCustomerId(Integer customerId) {
this.customerId = customerId;
}
#Id
#Column(name = "purchase_id")
public Integer getPurchaseId() {
return purchaseId;
}
public void setPurchaseId(Integer purchaseId) {
this.purchaseId = purchaseId;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
}
when I do a query using Criteria and using purchaseId and customerId as Restriction.eq filters, that's the query that gets generated:
select this_.customerId as customerId137_0_, this_.purchaseId as purchaseId137_0_, this_.count as count137_0_ from customer_purchases_counter this_ where this_.purchaseId=? and this_.customerId=?
that of course is wrong because the fields customerId and purchaseId are not renamed to their names that I specified using #Column????

Mapping seems to be correct. This is likely occurrence of HHH-4256 (Hibernate does not honor #Column(name=...) annotation with IdClass) . If so, then updating to the newer version of Hibernate offers solution.
Also according bug report using #Column annotation in IdClass is workaround.

I'm not sure if I understand correctly, but the #Column specified simply which column to map to your java data.
So you should not apply the #Column annotation to your getters, but rather to your variable declarations themselves.
#ID
#Column (name="customer_Id")
Integer customerId;
#ID
#Column (name="purchase_Id")
Integer purchaseId;

Related

Hibernate and ManyToMany + #OrderColumn returns 42075 Null records

when I use #OrderColumn annotation, Hibernate returns collection with 42075 [Null] records, but without #OrderColumn everything works perfectly why? I want to use field "OrderNumber" to have always ordered entity by this field. The type of this "OrderNumber" on PostgreSQL side is "serial" with auto increasing count.
DocTestEntity:
#Entity
#Table(name = "`Document`")
public class DocTestEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "`Document_ID`")
private Integer id;
#ManyToMany
#JoinTable(name = "`DocumentEmployee`",
joinColumns = #JoinColumn(name = "`Document_ID`"),
inverseJoinColumns = #JoinColumn(name = "`Employee_ID`"))
#OrderColumn(name ="`OrderNumber`", updatable = false, insertable = false)
private List<EmployeeTestEntity> employeeEntityList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<EmployeeTestEntity> getEmployeeEntityList() {
return employeeEntityList;
}
public void setEmployeeEntityList(List<EmployeeTestEntity> employeeEntityList) {
this.employeeEntityList = employeeEntityList;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DocTestEntity docEntity = (DocTestEntity) o;
return Objects.equals(id, docEntity.id);
}
#Override
public int hashCode() {
return Objects.hash(id);
}
#Override
public String toString() {
return "DocTestEntity{" +
"id=" + id +
'}';
}
}
EmployeeTestEntity:
#Entity
#Table(name = "`Employee`")
public class EmployeeTestEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "`Employee_ID`")
private Integer id;
#Column(name = "`Employee_name`")
private String name;
#Column(name = "`Employee_surname`")
private String surname;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String employeeName) {
this.name = employeeName;
}
public String getSurname() {
return surname;
}
public void setSurname(String employeeSurname) {
this.surname = employeeSurname;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmployeeTestEntity that = (EmployeeTestEntity) o;
return Objects.equals(id, that.id);
}
#Override
public int hashCode() {
return Objects.hash(id);
}
#Override
public String toString() {
return "EmployeeTestEntity{" +
"id=" + id +
'}';
}
}
And Test DAO:
#Stateless
public class DocTestDAO {
#PersistenceContext
private EntityManager em;
public DocTestEntity selectDocumentByID(Integer id) {
var result = em.createQuery("SELECT DISTINCT a from DocTestEntity a " +
" WHERE a.id = :id ", DocTestEntity.class)
.setParameter("id", id)
.getResultStream()
.map(Optional::ofNullable)
.findFirst()
.flatMap(Function.identity())
.orElse(null);
System.out.println("List records count is: " + result.getEmployeeEntityList().size());
return result;
}
}
You don't need the OrderNumber column to be autoincremented, the hibernate can manage the order column itself depending on the order of the items in the collection employeeEntityList.
You should make the OrderNumber to be insertable and updatable (true by default):
#OrderColumn(name = "OrderNumber")
I would recommend cleaning up the code, removing these apostrophes from the column names
"`DocumentEmployee`" should be "DocumentEmployee". You mentioned that everything could work without #OrderColumn, so I suppose apostrophes don't affect the functionality but look weird.
Please let me know if this still doesn't work after mentioned updates.

Saving entity tries to insert new record instead of merging with previous record

When a parent entity is persisted/merged via saveAndFlush, it tries to insert a new record for the child entity instead of finding/merging the existing record. This causes a SQLIntegrityConstraintViolationException error. I have also tried pulling the existing entity directly via the DAO, setting that to be the field in the parent entity, and then saving and flushing, and it still tries to insert a new record for the child entity field.
Any help is greatly appreciated!
Child Entity
#Entity
#Table(name = "DROPDOWN_TYPE", uniqueConstraints = {
#UniqueConstraint(columnNames = { "DROPDOWN_TYPE_TXT" }, name = "DROPDOWN_TYPE_TXT_UK") })
public class DropdownType {
#Id
#Column(name = "DROPDOWN_TYPE_TXT")
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DropdownType that = (DropdownType) o;
return text.equals(that.text);
}
#Override
public int hashCode() {
return text.hashCode();
}
}
Parent Entity
#Entity
#Table(name = "DROPDOWN", uniqueConstraints = {
#UniqueConstraint(columnNames = { "DROPDOWN_TXT", "DROPDOWN_TYPE_TXT" }, name = "DROPDOWN_UK") })
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Dropdown {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "DROPDOWN_OPTION_ID_GENERATOR")
#SequenceGenerator(allocationSize = 1, name = "DROPDOWN_OPTION_ID_GENERATOR", sequenceName = "DROPDOWN_OPTION_ID_SQ")
#Column(name = "DROPDOWN_OPTION_ID")
private Long id;
#ManyToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST })
#JoinColumn(name = "DROPDOWN_TYPE_TXT", foreignKey = #ForeignKey(name = "DROPDOWN_TYPE_TXT_FK"))
private DropdownType dropdownType;
#Column(name = "DROPDOWN_TXT")
private String text;
#Column(name = "ACTIVE_FLG")
private Boolean active;
#Column(name = "LEGACY_FLG")
private Boolean legacy;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public DropdownType getDropdownType() {
return dropdownType;
}
public void setDropdownType(DropdownType dropdownType) {
this.dropdownType = dropdownType;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Boolean isActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
public Boolean isLegacy() {
return legacy;
}
public void setLegacy(Boolean legacy) {
this.legacy = legacy;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dropdown dropdown = (Dropdown) o;
return dropdownType.equals(dropdown.dropdownType) && text.equals(dropdown.text);
}
#Override
public int hashCode() {
int result = dropdownType != null ? dropdownType.hashCode() : 0;
result = 31 * result + (text != null ? text.hashCode() : 0);
return result;
}
}
If you are using hibernate as your JPA provider, be careful when you override equals and hashcode -- see this post
It may be, that your JPA provider does not consider your entities to be equal as loaded entities can be some CGLIB proxies in reality (probably better to use instanceof than to compare classes).

InvalidDataAccessApiUsageException: Parameter value element did not match expected type

I'm trying to execute an IN query with by using Spring Data. My model looks like this:
#Entity
#Table(name = "customer", schema = "public", catalog = "postgres")
public class CustomerEntity {
private int id;
private String name;
private int balance;
private String bankId;
#Id
#Column(name = "id")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Basic
#Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Basic
#Column(name = "balance")
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
#Basic
#Column(name = "bank_id")
public String getBankId() {
return bankId;
}
public void setBankId(String bankId) {
this.bankId = bankId;
}
And my repository interface looks like this:
#Repository
public interface TransactionsRepository extends JpaRepository<TransactionsEntity, Long> {
List<TransactionsEntity> findByCustomerIdIn(List<CustomerEntity> customerEntities);
}
The problem is that when I try to execute this code
List<TransactionsEntity> transactionsEntitiesList = transactionsRepository.findByCustomerIdIn(customerEntitiesList);
I get this exception:
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value element [org.example.domain.admin.CustomerEntity#6a1a2a4] did not match expected type [java.lang.String (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value element [org.example.domain.admin.CustomerEntity#6a1a2a4] did not match expected type [java.lang.String (n/a)]
Update: TransactionsEntity.class:
#Entity
#Table(name = "transactions", schema = "public", catalog = "postgres")
public class TransactionsEntity {
private String id;
private String amount;
private String customerId;
#Id
#Column(name = "id")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Basic
#Column(name = "amount")
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
#Basic
#Column(name = "customer_id")
public String getCustomerId() {
return customerId;
}
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TransactionsEntity that = (TransactionsEntity) o;
if (id != null ? !id.equals(that.id) : that.id != null) return false;
if (amount != null ? !amount.equals(that.amount) : that.amount != null) return false;
if (customerId != null ? !customerId.equals(that.customerId) : that.customerId != null) return false;
return true;
}
#Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (amount != null ? amount.hashCode() : 0);
result = 31 * result + (customerId != null ? customerId.hashCode() : 0);
return result;
}
}
As it says in the exception Spring expects a String because your customer_id in your TransactionEntity is a String, but you are inputting a CustomerEntity. Instead you should input a List<String> with the list of your customer ids.
Btw shouldn't your customer_id be an int assuming you set it to the id of your CustomerEntity?
Then you could do something like
List<Integer> customerIds = customerEntitiesList.stream().map(CustomerEntity::getId).collect(Collectors.toList());

JPA query for one to one relation

I have an EJB application in where I am using Entity beans for database. I have to Entity beans having unidirectional one to one relation, JobPositionEntity and CandidateEntity.
Here is CandidateEntity
#Entity
public class CandidateEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Long meritNumber;
private String seatNumber;
private String candidateName;
private String gender;
public Long getMeritNumber() {
return meritNumber;
}
public void setMeritNumber(Long meritNumber) {
this.meritNumber = meritNumber;
}
public String getSeatNumber() {
return seatNumber;
}
public void setSeatNumber(String seatNumber) {
this.seatNumber = seatNumber;
}
public String getCandidateName() {
return candidateName;
}
public void setCandidateName(String candidateName) {
this.candidateName = candidateName;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
#Override
public Long getId() {
return id;
}
#Override
public void setId(Long id) {
this.id = id;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof CandidateEntity)) {
return false;
}
CandidateEntity other = (CandidateEntity) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.nisheeth.config.ejb.entity.CandidateEntity[ id=" + id + " ]";
}
}
Here is JobPositionEntity
#Entity
public class JobPositionEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToOne(fetch = FetchType.EAGER, orphanRemoval = true, cascade = CascadeType.ALL)
private CandidateEntity candidate;
#ManyToOne(fetch = FetchType.EAGER)
private SeasonEntity season;
public SeasonEntity getSeason() {
return season;
}
public void setSeason(SeasonEntity season) {
this.season = season;
}
public CandidateEntity getCandidate() {
return candidate;
}
public void setCandidate(CandidateEntity candidate) {
this.candidate = candidate;
}
#Override
public Long getId() {
return id;
}
#Override
public void setId(Long id) {
this.id = id;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof JobPositionEntity)) {
return false;
}
JobPositionEntity other = (JobPositionEntity) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "com.nisheeth.config.ejb.entity.JobPositionEntity[ id=" + id + " ]";
}
}
I want to select candidates which are not in JobPositionEntity. I have this query which did not work for me:
select ce.candidateName, ce.id from JobPositionEntity jp left join jp.candidate ce where ce <> null
Can anyone help write this query? Thanks in advance.
you can use a SubQuery
select c from Candidate c where c.id not in
(select jp.candidate.id from JobPositionEntity jp)
for more information:
https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html#queryhql-subqueries

Entity's equals() don't work properly in HashSet

Could you give me an idea mhy equals() doesn't work properly in POJO, when its entity is added to HashSet? I checked and hascode() works correctly because returns the same hashcode for entities with equal fields. But nevertheless equal objects are added in HashSet. Please take a look at the code below:
#Entity
#Table(name = "account")
public class Account {
private int accountID;
private String accountNumber;
private float amount;
private String currency;
private Client clientID;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "account_id")
public int getAccountID() {
return accountID;
}
public void setAccountID(int accountID) {
this.accountID = accountID;
}
#Column(name = "account_number")
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
#Column(name = "amount")
public float getAmount() {
return amount;
}
public void setAmount(float amount) {
this.amount = amount;
}
#Column(name = "currency")
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "client_id")
public Client getClientID() {
return clientID;
}
public void setClientID(Client clientID) {
this.clientID = clientID;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Account)) return false;
Account account = (Account) o;
if (Float.compare(account.amount, amount) != 0) return false;
if (!accountNumber.equals(account.accountNumber)) return false;
if (!clientID.equals(account.clientID)) return false;
if (!currency.equals(account.currency)) return false;
return true;
}
#Override
public int hashCode() {
int result = accountNumber.hashCode();
result = 31 * result + (amount != +0.0f ? Float.floatToIntBits(amount) : 0);
result = 31 * result + currency.hashCode();
result = 31 * result + clientID.hashCode();
return result;
}
}
result = 31 * result + clientID.hashCode();
I think with this line of code the hashCode of the two objects are not the same.Try to remove this line and test
thank you for pieces of advice but I realised what my mistake was. I didn't clarify that the application is used Hibernate for saving objects to DB. Objects are added in HashSet during particular Hibernate session. When I add an equal object to the one which is already in DB it happens in new Hibernate session with new empty HashSet.

Categories