2013-10-11 1 views
28

У меня есть таблица Stuff определяется как ...Обработка безалкогольных удалений с Spring JPA

id, <fields>..., active 

Активный является флаг мягкого удаления и всегда 1 или 0. В долгосрочной перспективе это может уйти в пользу исторической таблицы.

public interface StuffRepository extends JpaRepository<StuffEntity, Long> {} 

В коде, мы всегда использование активных записей. Есть ли способ заставить Spring всегда добавлять условие active=1 к запросам, сгенерированным для этого репозитория? Или, что еще лучше, позвольте мне расширить грамматику, используемую для генерации запросов?

Я понимаю, что я могу создать имя @queues всюду, но тогда я теряю удобство сгенерированных запросов. Я также хочу избежать загрязнения интерфейса «активными» методами.

Я использую Hibernate 4.2 в качестве моей реализации JPA, если это имеет значение.

ответ

45

Это старый вопрос, и вы, вероятно, уже нашли ответ. НО, для всех Spring/программистов JPA/Hibernate там ищет ответа -

Скажем у вас есть сущность собаки:

@Entity 
public class Dog{ 

......(fields)....   

@Column(name="is_active") 
private Boolean active; 
} 

и хранилище:

public interface DogRepository extends JpaRepository<Dog, Integer> { 
} 

Все, что вам нужно do добавляет аннотацию @Where на уровне сущности, в результате чего:

@Entity 
@Where(clause="is_active=1") 
public class Dog{ 

......(fields)....   

@Column(name="is_active") 
private Boolean active; 
} 

Все запросы p erformed репозиторием автоматически отфильтровывает «неактивные» строки.

+2

Я верю, что это центральный вопрос с гибернатом. Если у вас есть документы, которые показывают, что функция '@ Where' является функцией JPA или Spring, поделитесь ею. –

+2

Да, это решение Hibernate. Я упомянул об этом в первом абзаце ответа, но, по-видимому, я не был на 100% понятен. Итак - это решение использует аннотацию @Where Hibernate. Извините, спасибо за исправление. ПО ПУТИ - человек, который задал вопрос, использует спящий режим (4.2), что было основной причиной для меня ответа, который соответствует его потребностям. –

7

В текущих версиях (до 1.4.1) нет специальной поддержки для мягких удалений в Spring Data JPA. Тем не менее, я настоятельно рекомендую вам играть с веткой функций для DATAJPA-307, так как это функция, которая в настоящее время работает для предстоящего выпуска.

Чтобы использовать текущее состояние обновления, вы используете версию 1.5.0.DATAJPA-307-SNAPSHOT и убедитесь, что вы разрешили ей использовать специальную версию Spring Commons, которую она должна работать. Вы должны быть в состоянии следовать образцу test case, нам нужно посмотреть, как это работает.

P.S .: Я обновлю вопрос, как только закончим работу над этой функцией.

+1

С нетерпением жду этого. Вы делаете замечательную работу там Оливер! –

+7

Разве это сделало его выпуском? –

1

Предлагаю вам использовать представление базы данных (или эквивалент в Oracle), если вы не хотите импортировать аннотации спящего режима. В mySQL 5.5 эти представления могут быть обновляемыми и вставляемыми, если критерии фильтра просты, как активные = 1

создать или заменить вид active_stuff как выбрать * из Stuff, где активен = 1;

Является ли это хорошей идеей, возможно, зависит от вашей базы данных, но она отлично работает в моей реализации.

Восстановление удаленных требуется дополнительный объект, который доступ к «Материал» непосредственно, но тогда так бы @Where

30

@Where (п = «is_active = 1») не самый лучший способ справиться с мягким удалением с JPA пружинные данных.

Во-первых, он работает только с устройством спящего режима.

Во-вторых, вы никогда не сможете получать мягкие удаленные объекты с данными весны.

Мое решение - el, предоставленное данными весны. Выражение # {# entityName} может использоваться для общего репозитория, представляющего конкретное имя типа объекта.

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

enter code here 

//Override CrudRepository or PagingAndSortingRepository's query method: 
@Override 
@Query("select e from #{#entityName} e where e.deleteFlag=false") 
public List<T> findAll(); 

//Look up deleted entities 
@Query("select e from #{#entityName} e where e.deleteFlag=true") 
public List<T> recycleBin(); 

//Soft delete. 
@Query("update #{#entityName} e set e.deleteFlag=true where e.id=?1") 
@Modifying 
public void softDelete(String id); 
+0

Активируйте свой ответ, не зная, почему он не сверху, потому что он отвечает на вопрос в самой дружественной форме JPA/Spring. Спасибо. – Max

+0

Что делать, если e.id не является «id», но вместо этого «userId» или «accountId» и т. Д. Это будет работать, или мне нужно добавить этот метод ко всем моим репозиториям? – cosbor11

+0

Данные SpEL in spring не поддерживают переменную, представляющую собой идентификатор. Поэтому переопределите эти методы, если ваш идентификатор объекта не имеет имени id. Думаю, большая часть вашей сущности будет называться id. –

1

Вы можете простираться от SimpleJpaRepository и создать свой собственный репозиторий, где вы можете определить программную функциональность delere в общем виде.

Вам также необходимо создать пользовательский JpaRepositoryFactoryBean и включить его в свой основной класс.

Вы можете проверить свой код здесь https://github.com/dzinot/spring-boot-jpa-soft-delete

8

Основываясь на 易天明 ответа я создал CrudRepository реализации с переопределенными методами для мягкого удаления:

@NoRepositoryBean 
public interface SoftDeleteCrudRepository<T extends BasicEntity, ID extends Long> extends CrudRepository<T, ID> { 
    @Override 
    @Transactional(readOnly = true) 
    @Query("select e from #{#entityName} e where e.isActive = true") 
    List<T> findAll(); 

    @Override 
    @Transactional(readOnly = true) 
    @Query("select e from #{#entityName} e where e.id in ?1 and e.isActive = true") 
    Iterable<T> findAll(Iterable<ID> ids); 

    @Override 
    @Transactional(readOnly = true) 
    @Query("select e from #{#entityName} e where e.id = ?1 and e.isActive = true") 
    T findOne(ID id); 

    //Look up deleted entities 
    @Query("select e from #{#entityName} e where e.isActive = false") 
    @Transactional(readOnly = true) 
    List<T> findInactive(); 

    @Override 
    @Transactional(readOnly = true) 
    @Query("select count(e) from #{#entityName} e where e.isActive = true") 
    long count(); 

    @Override 
    @Transactional(readOnly = true) 
    default boolean exists(ID id) { 
     return findOne(id) != null; 
    } 

    @Override 
    @Query("update #{#entityName} e set e.isActive=false where e.id = ?1") 
    @Transactional 
    @Modifying 
    void delete(Long id); 


    @Override 
    @Transactional 
    default void delete(T entity) { 
     delete(entity.getId()); 
    } 

    @Override 
    @Transactional 
    default void delete(Iterable<? extends T> entities) { 
     entities.forEach(entitiy -> delete(entitiy.getId())); 
    } 

    @Override 
    @Query("update #{#entityName} e set e.isActive=false") 
    @Transactional 
    @Modifying 
    void deleteAll(); 
} 

Он может быть использован с BasicEntity:

@MappedSuperclass 
public abstract class BasicEntity { 
    @Column(name = "is_active") 
    private boolean isActive = true; 

    public abstract Long getId(); 

    // isActive getters and setters... 
} 

И конечное лицо:

@Entity 
@Table(name = "town") 
public class Town extends BasicEntity { 

    @Id 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "town_id_seq") 
    @SequenceGenerator(name = "town_id_seq", sequenceName = "town_id_seq", allocationSize = 1) 
    protected Long id; 

    private String name; 

    // getters and setters... 
} 
+2

Можно ли интегрировать это с PagingAndSortingRepository? –

+0

Как бы вы отменили поиск на странице findAll (например, страницы)? – alex