有没有办法引用规格(Spring)的审计表?
Posted
技术标签:
【中文标题】有没有办法引用规格(Spring)的审计表?【英文标题】:Is there a way to reference an audit table for Specifications (Spring)? 【发布时间】:2020-06-02 20:52:38 【问题描述】:我一直在寻找与此类似的问题,但没有成功。
我的目标:我需要获取创建某个实体的用户名,该用户名存储在通过 Spring 的 @AuditTable 引用的该实体的审计表中。
有点(希望)解释:在这种特殊情况下,我将添加到使用 org.springframework.data.jpa.repository.JpaSpecificationExecutor 类以及 Predicate 类(javax.persistence.criteria)构建的先前存在的查询中。谓词)。连接倾向于使用类的名称(例如 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);
一个可能的解决方案:我正在考虑添加一个引用被审计实体的新实体,如下所示:
@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
;
我会努力完成这项工作。有没有办法做到这一点?我应该只添加一个执行此操作的本机查询吗? 谢谢!
【问题讨论】:
【参考方案1】:一种解决方案是将 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
的任何其他属性一样。
【讨论】:
我还有几个问题要问,如果可以的话。视图性能有多好?如果我想扩大规模,它仍然是一个不错的选择,还是返工会更好? (不使用规范并进行本机查询)。另外,什么时候查询视图?是当我从 person 表中查询 Person 对象时(例如使用 findByIds(ids) 方法查询),还是当我对 getCreatedBy() 之类的属性使用 getter 时?尽管如此,我还是会尝试一下。我既不知道视图也不知道 @SecondaryTable 注释,非常感谢! 抱歉,我还有一个问题。我看到通常@SecondaryTable 通过外键连接到第一个表。既然这是一个视图,我怎么能指定这个人的 id 和视图行的 id 是一样的呢?还是它会自动执行?出于好奇,如果我不按 revtype 过滤(并为那个人带来所有审计值)会发生什么; Spring 会将其映射到多个结果吗?【参考方案2】:最后,我找不到正确引用审计表的方法,所以不得不使用 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 时使用了该哈希图。
【讨论】:
以上是关于有没有办法引用规格(Spring)的审计表?的主要内容,如果未能解决你的问题,请参考以下文章
刚刚使用spring boot 在写service的时候,没办法注入。有大神帮我看看吗?