Есть ли способ ссылаться на таблицу аудита для спецификаций (Spring)? - PullRequest
0 голосов
/ 18 февраля 2020

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

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

Немного (надеюсь) объяснил: в этой конкретной ситуации я добавляю к ранее существующему запросу, созданному с помощью класса org.springframework.data.jpa.repository.JpaSpecificationExecutor, а также класс предиката (javax.persistence.criteria.Predicate). Соединения, как правило, производятся с именем класса (например, Join), из которого Spring получает имя таблицы. У меня вопрос, есть ли способ заставить Spring выглядеть по имени таблицы аудита вместо имени таблицы сущности?

Примером может быть:

@Entity @Table(name = "persons")
@Audited @AuditTable(name = "audit_persons")
public class Person {
        private String id;
        private String name;
        private Job job;
        /*getters and setters*/
}

@Entity @Table(name = "jobs")
@Audited @AuditTable(name = "audit_jobs")
public class Job {
        private String id;
        private String name;
        /*getters and setters*/
}

public final class PersonSpecification {
    private PersonSpecification(){}

    public static Specification<Person> findBy(final String attributeName, final Object filterValue) {
        return new Specification<Person>() {
            @Override
            public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                return cb.equal(root.get(attributeName), filterValue);
            }
        };
    }

    public static Specification<Person> findByJob(final Job job) {
        return new Specification<Person>() {

            @Override
            public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

                Join<Person, Job> joinJob = root.join("job", JoinType.LEFT);
                return cb.equal(joinJob.<Job>get("job"), job);
            }
        };
    }

/*
    public static Specification<Person> findByUsername(final String username) {
        return new Specification<Person>() {
            @Override
            public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Join<Person, audit person?> join? = root.join(?, JoinType.LEFT);
                return I don't know how to access;
            }
        };
    }
*/
}

import static ().PersonSpecification.findBy;
import static ().PersonSpecification.findByJob;
@Service(value="personService")
public class PersonService {

    @Autowired
    private PersonDao personDao;

    public List<Person> find(String id, String name, Job job /*, String username*/){

        Specifications<Person> specifications = null;

        if (!StringUtils.isBlank(id) && StringUtils.isNumeric(id)) {
            specifications = loadFilter(specifications, findBy("id", id));
        }else{
            if (!StringUtils.isBlank(name)) {
                specifications = loadFilter(specifications, findBy("name", name));
            }
            if (job != null) {
                specifications = loadFilter(specifications, findByJob("job", job));
            }
            /*
            if (!StringUtils.isBlank(username)) {
                specifications = I don't know what to do here;
            }
            */
        }
        return personDao.findAll(specifications);
    }
}

Возможное решение: I думал о добавлении новой сущности, которая будет ссылаться на проверяемую сущность, например:

@Entity @Table(name = "audit_persons")
public class AuditedPerson {
       private String id;
       private Integer rev;
       private Integer revtype;
       private String username;
       /*getters and setters*/
}

И тогда мой метод будет

public static Specification<Person> findByUsername(final String username) {
       return new Specification<Person>() {
           @Override
           public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
               Join<Person, AuditedPerson> joinAudit = root.join(id, JoinType.LEFT);
               return cb.and(cb.equal(joinAudit.get("username"), username),
                               cb.equal(joinAudit.get("revtype"), 0)); //this is so I get the creation instance's username
           }
       };
   }

Я попытаюсь сделать эту работу , Уже есть способ сделать это? Должен ли я просто добавить собственный запрос, который делает это? Спасибо!

Ответы [ 2 ]

0 голосов
/ 06 марта 2020

В конце концов, я не смог найти способ правильно ссылаться на таблицу аудита, поэтому пришлось использовать hibernate enver для доступа к ней. Я не буду отмечать это как ответ, так как это не соответствует тому, что я искал, но я все равно добавлю это, чтобы я знал, что я сделал. Что я сделал (для дальнейшего использования):

@Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED, readOnly = true)
public Map<Long,String> getCreatorUsernameByPersonId(List<Long> personIds) {

    Map<Long,String> result = new HashMap<Long,String>();
    EntityManager em = entityManagerFactory.createEntityManager();
    List<?> auditedPeople = null;
    try {
        AuditReader reader = AuditReaderFactory.get(em);
        AuditQuery query = reader.createQuery().forRevisionsOfEntity(Person.class, false, false);
        if(personIds != null && !personIds.isEmpty()) query.add(AuditEntity.id().in(personIds));
        query.add(AuditEntity.revisionType().eq(RevisionType.ADD));
        auditedPeople = query.getResultList();
        if(auditedPeople != null && !auditedPeople.isEmpty()){
            for(Object o : auditedPeople) {
                result.put(((Person)((Object[])o)[0]).getId(), ((AuditRevEntity)((Object[])o)[1]).getUsername());
            }
        }
    } finally {
        em.close();
    }
    return result;
}

Затем я использовал эту хэш-карту при создании dto для того, что мне нужно.

0 голосов
/ 19 февраля 2020

Одним из решений было бы использование JPA @SecondaryTable с представлением базы данных.

Во-первых, создайте представление (скажем, vw_person_additional_data) из таблицы аудита, которая выбирает только начальную «созданную» запись для каждого человека.

Обновите вашу личность, как показано ниже. Аннотация @SecondaryTable позволяет нам сопоставить сущность с одной или несколькими таблицами или представлениями.

@Entity 
@Table(name = "persons")
@Audited 
@AuditTable(name = "audit_persons")
//secondary table referencing our new view
@SecondaryTable(name = "vw_person_additional_data", 
                   pkJoinColumns = {@PrimaryKeyJoinColumn(name = "person_id", 
                        referencedColumnName = "id")}) 
public class Person {
        private String id;
        private String name;
        private Job job;

        @Column(name = "username", table="vw_person_additional_data") //column in view
        private String createdBy;
}

Свойство createdBy теперь аналогично любому другому свойству Person, когда речь идет о написании спецификаций.

...