2017-02-08 2 views
0

У меня есть одна из странных проблем, которые заставляют вас рычать. У меня есть (тоже странное) приложение Spring/Hibernate, предназначенное для управления базой данных следующим образом (я упростил некоторые вещи, поэтому не путайте, что в исходном коде упоминаются несколько разные таблицы/столбцы):Спящий режим: не удалять дочернюю коллекцию на родительском удалении

active_proxy_view table: 
id  | entity 
<uuid> | <string containing json> 

archive_proxy_view table: 
id  | entity 
<uuid> | <string containing json> 

track_reference table: 
ref_type | ref_id | track_domain | track_type | track_id | 
'proxy' | <uuid> | 'example.com' | 'customer' | '123' | 

Сохранение двух таблиц является обязательным - мне нужно иметь как все время-историю/статистические запросы, так и запросы бизнес-значений только для вещей, которые сейчас активны, поэтому мне нужно постоянно устанавливать активные активные прокси. track_reference таблица используется для поиска, так что я мог сделать запросы так:

SELECT p.id, p.entity FROM archive_proxy_view AS p 
INNER JOIN track_reference AS t1 ON 
    t1.ref_id = p.id AND 
    t1.ref_type = 'proxy' AND 
    t1.track_domain = 'example.com' AND 
    t1.track_type = 'customer' AND 
    t1.track_id = '123' 
INNER JOIN track_reference AS t2 ON 
    t2.ref_id = p.id AND 
    t2.ref_type = 'proxy' AND 
    t2.track_domain = 'example.com' AND 
    t2.track_type = 'campaign' AND 
    t2.track_id = 'halloween-2017' 

(это может быть не 100% правильно, я не сырой опыт SQL в то время)

И вот проблема:

  • Оба active_proxy_view и archive_proxy_view объекты наследуются от одного класса, который определяет @OneToMany отношений O n track_reference объект; @ManyToOne использование не представляется возможным, потому что есть много сущностей, привязанные к отслеживанию, присвоенный
  • track_reference управляется отдельно (и это обязательно тоже)
  • мне нужно управлять видом отдельно от track_reference таблицы, но всякий раз, когда я говорю Hibernate, чтобы удалить объект от active_proxy_view стол, он убирает track_reference объектов. Даже если я играю с каскадным значением аннотации, которое по умолчанию пусто (и, как я понимаю, это означает, что дочерние записи не следует удалять родителем). Однако есть вероятность, что я что-то пропустил.
  • я также не смог взломать все это с помощью пользовательских @SQLDeleteAll, я до сих пор можно увидеть регулярные удалений в общем журнале:

    55 Query  delete from tracking_reference where referenced_entity_id='13c6b55c-f9b7-4de7-8bd4-958d487e461c' and referenced_entity_type='proxy' and tracked_entity_type='agent' 
    55 Query  delete from tracking_reference where referenced_entity_id='13c6b55c-f9b7-4de7-8bd4-958d487e461c' and referenced_entity_type='proxy' and tracked_entity_type='lead' 
    55 Query  delete from tracking_reference where referenced_entity_id='13c6b55c-f9b7-4de7-8bd4-958d487e461c' and referenced_entity_type='proxy' and tracked_entity_type='source' 
    53 Query  DELETE FROM `tracking_reference` WHERE `referenced_entity_type` = 'proxy' AND referenced_entity_id = '13c6b55c-f9b7-4de7-8bd4-958d487e461c' AND 1 = 0 
    
  • Я использую Hibernate 5.2.3.Final через Spring 4.3.2. RELEASE/Spring Data JPA 1.10.2.RELEASE

TL; DR

Итак, возникает вопрос: как я могу запретить Hibernate удалять связанные объекты, когда родитель удален?

Исходный код для субъектов выглядит следующим образом:

@Entity 
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 
public class ProxyViewEntryTemplate { 

    @Id 
    @NotNull 
    @Column(nullable = false) 
    private String id; 

    @NotNull 
    @Column 
    private String entity; 

    // some other columns 

    @OneToMany 
    @JoinColumn(name = TrackRef.REFERENCE_ID_COLUMN_NAME) // 'reference_entity_id` 
    @Where(clause = ProxyView.TRACK_WHERE_JOIN_CLAUSE) // `referenced_entity_type` = 'proxy' 
    @SQLDeleteAll(sql = ProxyView.TRACK_DELETE_ALL_QUERY) // DELETE FROM `tracking_reference` WHERE `referenced_entity_type` = 'proxy' AND referenced_entity_id = ? AND 1 = 0 
    private Collection<TrackingReference> track = new ArrayList<>(); 

    // setters, getters, hashCode, equals 
} 

@Entity 
@Table(name = "active_proxy") 
public class ActiveProxyViewEntry extends ProxyViewEntryTemplate {} 


@Entity 
@Table(name = "tracking_reference") 
@IdClass(TrackingReferenceId.class) 
public class TrackingReference { 

    @Id 
    @Column(name = "tracked_entity_type", nullable = false) 
    @NotNull 
    private String trackedType; 

    @Id 
    @Column(name = "tracked_entity_domain", nullable = false) 
    private String trackedDomain; 

    @Id 
    @Column(name = "tracked_entity_id", nullable = false) 
    private String trackedId; 

    @Id 
    @Column(name = "referenced_entity_type", nullable = false) 
    @NotNull 
    private String referencedType; 

    @Id 
    @Column(name = "referenced_entity_id", nullable = false) 
    @NotNull 
    private String referencedId; 

    // setters, getters, hashCode, equals 
} 

Все это управляется с помощью Spring JPA Хранилища:

@NoRepositoryBean 
public interface SuperRepository<E, ID extends Serializable> extends PagingAndSortingRepository<E, ID>, 
     JpaSpecificationExecutor<E> { 
} 

public interface ActiveProxyViewRepository extends SuperRepository<ActiveProxyViewEntry, String> {} 

// the call for deletion 
public CompletableFuture<Void> delete(ID id) { 
    ... 
    descriptor.getRepository().delete(descriptor.getIdentifierConverter().convert(id)); 
    ... 
} 
// which is equal to 
... 
ActiveProxyViewRepository repository = descriptor.getRepository(); 
String uuidAsString = descriptor.getIdentifierConverter().convert(id); 
repository.delete(uuidAsString); 
... 
+0

Пожалуйста, укажите код, показывающий, как вы удаляете объект. – Naros

+0

@ Naros сделано, хотя не уверен, что это поможет много – Etki

ответ

0

Если удалить @JoinColumn, вы не должны иметь эту проблему ,

Если вам нужно сохранить @JoinColumn, вам необходимо удалить требование внешнего ключа, который автоматически применяется поставщиком инерционности путем изменений аннотации:

@JoinColumn(
    name = "yourName" 
    foreignKey = @Foreignkey(value = ConstraintMode.NO_CONSTRAINT) 
) 

Затем вы должны быть в состоянии удалить объект представления, не заставляя ссылки отслеживания удаляться.

+0

К сожалению, внешний ключ не работает. Каковы мои параметры, если я хочу попробовать подход remove-join-column? Я использую Criteria API для построения запроса, как указано в вопросе, и, если я правильно понимаю/помню его, в проекте был добавлен '@ JoinColumn', чтобы разрешить объединение с помощью API критериев. – Etki

0

Оказалось, что это типичная стрельба по сценарию стопы.

ссылки Tracking были обновлены в довольно сложным способом:

  • коллекция Сложение ссылок, которые будут храниться в базе данных (C1)
  • Загрузить все присутствующие ссылки (C2)
  • магазин C1
  • Удалите все ссылки, которые присутствуют в С2, но не указаны в С1 (с использованием collection.removeAll)

И оказалось, что мой метод .equals был написан ужасно неправильно, возвращая ложь почти в каждом случае. Из-за этого, как правило, каждая ссылка была удалена из базы данных (запросы, которые вы можете видеть в журнале), так что это была моя ошибка.

После того, как я исправил это, был выполнен запрос только @SQLDeleteAll - по неизвестным мне причинам он по-прежнему действовал, как если бы был установлен параметр каскада. Мне удалось избавиться от него, используя @OneToMany(updatable = false, insertable = false); это похоже на грязный хак, но у меня нет достаточно времени, чтобы прорвать его.

Я еще не проверил его, но, надеюсь, это решает проблему.