I need to use a database where 2 tables of some custom foreign key. It will reference one field under certain condition and another field otherwise.
How do I annotate this using Hibernate ?
Here are the tables.
--------------------------- ---------------------------
| customer | | address |
--------------------------- ---------------------------
| - id | | - address_code |
| address_code | | - customer_id |
| primary_language_code | | - language_code |
| secondary_language_code | | ... |
| ... | | |
--------------------------- ---------------------------
Fields beginning with -
are (part of) primary key. Foreign key is as follow :
customer join address on
(customer.id = address.customer_id
and customer.address_code = address.address_code
and (case when customer.primary_language_code <= 2
then customer.primary_language_code
else customer.secondary_language_code
end) = address.COD_LAN)
Here are the models :
@Getter \
@Setter | //lombok
@NoArgsConstructor /
@Entity
@Table(name = "customer")
public class Customer{
@Id
@Column(name="id")
private int id;
@Column(name="primary_language_code")
private int primaryLanguageCode;
@Column(name="secondary_language_code")
private int secondaryLanguageCode;
?
private Address address;
}
@Getter \
@Setter | //lombok
@NoArgsConstructor /
@Entity
@Table(name = "address")
public class Address {
@EmbeddedId
private AddressId id;
}
@Getter \
@Setter | //lombok
@NoArgsConstructor /
@Embeddable
public class AddressId implements Serializable, Comparable<AddressId> {
@Column(name = "customer_id")
private int customerId;
@NonNull
@Column(name = "address_code")
private String addressCode;
?
private int languageCode;
}
Edit :
customer0_.address_primary_language_code
:@Where(clause = "(customer.primary_language_code <= 2 then customer.primary_language_code else customer.secondary_language_code end) = address.COD_LAN)")
So, I found some creepy hack using sql injection that solves my problem but can be prone to bugs in some conditions.
If join was only on primary_language
, code would be like this :
@OneToOne
@JoinColumns({@JoinColumn(name = "id", referencedColumnName = "customer_id"),
@JoinColumn(name = "primary_language_code", referencedColumnName = "language_code"),
@JoinColumn(name = "address_code", referencedColumnName = "address_code")})
private Address address;
@Column(name = "language_code", nullable = false)
private int languageCode;
Hibernate will generate a query like this :
select customer0_.id as ...,
customer0_.primary_language_code as ...,
customer0_.secondary_language_code as ...,
customer0_.address_code as ...
The idea is to change the name value to inject sql code :
@OneToOne
@JoinColumns({@JoinColumn(name = "id", referencedColumnName = "customer_id"),
@JoinColumn(name = "id, case when customer.primary_language_code <= 2 " +
"then customer.primary_language_code " +
"else customer.secondary_language_code end",
referencedColumnName = "language_code"),
@JoinColumn(name = "address_code", referencedColumnName = "address_code")})
private Address address;
Generated sql query will look like this :
select customer0_.id as ...,
customer0_.primary_language_code as ...,
customer0_.secondary_language_code as ...,
customer0_.id, <--- sql injection here
case
when customer.primary_language_code <= 2 then customer.primary_language_code
else customer.secondary_language_code
end as ...,
customer0_.address_code as ...
This will work unless you have primary_language or secondary_language in another table you need to join on the customer table.
I'd be glad to find a better solution to this question as I don't really dare to use it in production.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.