Спящий пользовательский внешний ключ - PullRequest
1 голос
/ 15 января 2020

Мне нужно использовать базу данных, где 2 таблицы какого-то пользовательского внешнего ключа. Он будет ссылаться на одно поле при определенных условиях, а другое - на другое.

Как мне аннотировать это с помощью Hibernate?

Вот таблицы.

---------------------------                      ---------------------------
|         customer        |                      |         address         |
---------------------------                      ---------------------------
| - id                    |                      | - address_code          |
| address_code            |                      | - customer_id           |
| primary_language_code   |                      | - language_code         |
| secondary_language_code |                      | ...                     |
| ...                     |                      |                         |
---------------------------                      ---------------------------

Поля, начинающиеся с - являются (частью) первичным ключом. Внешний ключ выглядит следующим образом:

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)

Вот модели:

@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;
}

Редактировать:

  • Я попытался использовать вычисляемое поле и карту из этого поле. Проблема: как я могу отобразить это вычисленное поле?
  • Я пытался использовать предложение @Where, но имя поля модели объединяется в sql поле запроса 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)")
  • Я попробовал несколько других вещей, но без особого успеха.

1 Ответ

0 голосов
/ 16 января 2020

Итак, я нашел какой-то жуткий взлом с использованием sql инъекции, который решает мою проблему, но может быть склонен к ошибкам в некоторых условиях.

Если объединение было только на primary_language, код был бы таким:

@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 сгенерирует запрос, подобный следующему:

select customer0_.id as ...,
       customer0_.primary_language_code as ...,
       customer0_.secondary_language_code as ...,
       customer0_.address_code as ...

Идея состоит в том, чтобы изменить значение имени для ввода sql код:

@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;

Сгенерированный sql запрос будет выглядеть следующим образом:

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 ...

Это будет работать, если у вас нет primary_language или Secondary_language в другой таблице, которую нужно объединить в таблице клиента.

I Я был бы рад найти лучшее решение этого вопроса, так как я не смею использовать его в производстве.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...