Ссылка на статическую таблицу без внешнего ключа при сохранении

Предположим, что у меня есть два объекта (ommitted getters / setters):

@Entity
@Table
public class A {

    private Long id;
    private B b;
    private String details;

    @SequenceGenerator(name = "auto_gen", sequenceName = "a_sequence", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "auto_gen")
    @Column(name="id",updatable = false,nullable = false)
    @Id
    public Long getId(){return id;}

    @OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
    @JoinColumn(name = "a_id")
    public B getB(){
        return b;
    }

}


@Entity
@Table
public class B {
    @Id
    @GenericGenerator(name="gen",strategy = "BIdentityGenerator")
    @GeneratedValue(generator = "gen")
    private Long id;
    @Column
    private String reason;
    @Column
    private Boolean requiresDetails;
}

И обе таблицы связаны так:

Table A
-------
id
b_id (foreign key to primary key in B)
details

Table B
-------
id
reason (unique)

В таблице B хранятся статические данные , поэтому новые строки никогда не должны быть вставлены. Учитывая, что у меня есть новый объект A и отношение сущности к существующему объекту Bb B, я хотел бы сохранить A как новую строку и ссылку на существующую строку для таблицы B. Я также хотел бы сохранить A без идентификатора B, только уникальное значение reason.

Этот сценарий прекрасен, если я вручную предоставил идентификатор для B перед сохранением A, но кодирование этого способа кажется немного громоздким. Мне было интересно, есть ли способ сделать это через генератор id, чтобы неявно установить идентификатор B, заданный значением reason в базе данных. Я попытался использовать GenericGenerator для генерации идентификатора (см. Ниже); однако в этой ситуации возникает проблема, когда предполагается, что сгенерированное значение является полностью новым сгенерированным идентификатором и нарушает ограничение первичного ключа. Я также рассмотрел использование слушателей сущностей, но в документах указано, что они не должны называть EntityManager.

public class BIdentityGenerator implements IdentifierGenerator {

    private static final String QUERY = "SELECT id FROM b WHERE reason = ?";
    private static Logger log = LoggerFactory.getLogger(BIdentityGenerator.class);

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object o) throws HibernateException {

        if (o instanceof B) {
            B b = (B) o;
            if (b.getReason() == null || b.getReason().isEmpty()) {
                throw new HibernateException("No reason provided.");
            }
            try {
                Connection connection = session.connection();
                PreparedStatement statement = connection.prepareStatement(QUERY);
                statement.setString(1, b.getReason());
                ResultSet resultSet = statement.executeQuery();
                if (resultSet.next()) {
                    long id = resultSet.getLong("id");
                    return id;
                } else {
                    throw new HibernateException(String.format("No row with reason %s exists.", b.getReason()));
                }
            } catch (SQLException e) {
                throw new HibernateException(String.format("Unable to retrieve reason %s", b),e);
            }
        } else {
            throw new HibernateException("BIdentityGenerator only supports B.");
        }

    }
}
0
задан 13 August 2018 в 14:48

1 ответ

вы должны удалить CascadeType.ALL из Entity A. у вас должен быть DAO, который возвращает вас Entity B на основе reason, что-то вроде entityA.setEntityB(entityBDao.findBy(reason))

0
ответ дан 15 August 2018 в 17:02
  • 1
    Спасибо, это то, что я реализовал ранее, но хотел использовать неявное решение, вместо того, чтобы явно связывать B с A. – Andrew Burrows 13 August 2018 в 15:54

Другие вопросы по тегам:

Похожие вопросы: