草庐IT

java - 如何从 customRevisionEntity 检索用户名

coder 2023-09-02 原文

我们正在使用 Hibernate-envers 3.6.3.Final,我正在使用 @Audited 注释正确生成表格。我正在使用 CustomRevisionEntity 来存储用户信息,而 CustomRevisionListenner 也存储用户信息。但是如果我试图检索“用户名”,它会返回以下错误。

org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
    at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:64) ~[spring-orm-3.2.6.RELEASE.jar:3.2.6.RELEASE]

我的 CustomRevisionEntity 类,

@Entity
@Table(name = "revision_info")
@RevisionEntity(CustomEnversListener.class)
public class CustomRevisionEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    @RevisionNumber
    private int id;

    @RevisionTimestamp
    private long timestamp;

    private String username;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public long getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(long timestamp) {
        this.timestamp = timestamp;
    }

    @Column(name = "username")
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

CustomRevisionListener.java

public class CustomEnversListener implements RevisionListener {
    public void newRevision(Object revisionEntity) {
        CustomRevisionEntity customRevisionEntity = (CustomRevisionEntity) revisionEntity;
        Authentication authentication = SecurityContextHolder.getContext()
                .getAuthentication();
        customRevisionEntity.setUsername(authentication.getName());
    }
}

我的表格如下,

mysql> select * from revision_info;
+----+---------------+-----------------+
| id | timestamp     | username        |
+----+---------------+-----------------+
|  1 | 1431693146030 | sky@test.com    |
|  2 | 1431693150805 | ram@test.com    |
|  3 | 1431693164895 | bobo@test.com   |
+----+---------------+-----------------+
3 rows in set (0.02 sec)

我可以使用“timeStamp”检索“rev”,使用“rev”检索“timeStamp”,使用以下代码,

AuditReader reader = AuditReaderFactory.get(session);
Date timestamp = reader.getRevisionDate(rev);
Number revision = reader.getRevisionNumberForDate(timestamp);

但我无法使用 hibernate 查询检索具有自定义字段“用户名”值的整行。

Criteria criteria = sessionFactory.getCurrentSession()
                .createCriteria(CustomRevisionEntity.class)
                .add(Restrictions.eq("id", rev));

上面的查询返回上面的错误。我该如何解决这个问题?? 如何从 revision_info 表中检索值?

我的错误的完整堆栈跟踪是,

org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
    at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:64) ~[spring-orm-3.2.6.RELEASE.jar:3.2.6.RELEASE]
    at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:687) ~[hibernate-core-3.6.3.Final.jar:3.6.3.Final]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:701) ~[spring-aop-3.2.6.RELEASE.jar:3.2.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) ~[spring-aop-3.2.6.RELEASE.jar:3.2.6.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91) ~[spring-aop-3.2.6.RELEASE.jar:3.2.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) ~[spring-aop-3.2.6.RELEASE.jar:3.2.6.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:634) ~[spring-aop-3.2.6.RELEASE.jar:3.2.6.RELEASE]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_31]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.6.0_31]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.6.0_31]
    at java.lang.reflect.Method.invoke(Method.java:622) ~[na:1.6.0_31]
    at ognl.OgnlRuntime.invokeMethod(OgnlRuntime.java:870) ~[ognl-3.0.6.jar:na]
    at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1293) ~[ognl-3.0.6.jar:na]
    at ognl.ObjectMethodAccessor.callMethod(ObjectMethodAccessor.java:68) ~[ognl-3.0.6.jar:na]
    at com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor.callMethodWithDebugInfo(XWorkMethodAccessor.java:117) [xwork-core-2.3.20.jar:2.3.20]
    at com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor.callMethod(XWorkMethodAccessor.java:108) [xwork-core-2.3.20.jar:2.3.20]
    at ognl.OgnlRuntime.callMethod(OgnlRuntime.java:1369) ~[ognl-3.0.6.jar:na]
    at ognl.ASTMethod.getValueBody(ASTMethod.java:90) ~[ognl-3.0.6.jar:na]
    at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212) ~[ognl-3.0.6.jar:na]
    at ognl.SimpleNode.getValue(SimpleNode.java:258) ~[ognl-3.0.6.jar:na]
    at ognl.Ognl.getValue(Ognl.java:494) ~[ognl-3.0.6.jar:na]
    at ognl.Ognl.getValue(Ognl.java:458) ~[ognl-3.0.6.jar:na]
    at com.opensymphony.xwork2.ognl.OgnlUtil$2.execute(OgnlUtil.java:309) ~[xwork-core-2.3.20.jar:2.3.20]
    at com.opensymphony.xwork2.ognl.OgnlUtil.compileAndExecute(OgnlUtil.java:340) ~[xwork-core-2.3.20.jar:2.3.20]
    at com.opensymphony.xwork2.ognl.OgnlUtil.getValue(OgnlUtil.java:307) ~[xwork-core-2.3.20.jar:2.3.20]
    at com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:423) ~[xwork-core-2.3.20.jar:2.3.20]
    at com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:287) ~[xwork-core-2.3.20.jar:2.3.20]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:250) ~[xwork-core-2.3.20.jar:2.3.20]
    at org.apache.struts2.interceptor.DeprecationInterceptor.intercept(DeprecationInterceptor.java:41) ~[struts2-core-2.3.20.jar:2.3.20]

我的spring配置如下,

    <bean id="transactionManager"
              class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="xxxSessionFactory"/>
    </bean>

    <tx:advice id="customRevisionEntityAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="*" read-only="false" propagation="REQUIRED"/>
            </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="crePointcut"
                      expression="execution(* bla.bla.CustomRevisionEntity.*(..))"/>
        <aop:advisor advice-ref="customRevisionEntityAdvice" pointcut-ref="crePointcut"/>
    </aop:config>

我的 application-content.xml 包含以下...

<bean id="auditEventListener" class="org.hibernate.envers.event.AuditEventListener" />

    <bean id="xxxSessionFactory"
          class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="annotatedClasses">
            <list>                  
                <value>bla.bla.domain.Myclass</value>
            </list>
        </property>

        <property name="eventListeners">
            <map>
                <entry key="post-insert" value-ref="auditEventListener"/>
                <entry key="post-update" value-ref="auditEventListener"/>
                <entry key="post-delete" value-ref="auditEventListener"/>
                <entry key="pre-collection-update" value-ref="auditEventListener"/>
                <entry key="pre-collection-remove" value-ref="auditEventListener"/>
                <entry key="post-collection-recreate" value-ref="auditEventListener"/>
            </map>
        </property>

最佳答案

使用恩弗斯

您可以使用 AuditReader 查询 CustomRevisionEntity

AuditReader auditReader = AuditReaderFactory.get(entityManager);

//Here you find the revision number that you want
Number revisionNumber = getRevisionNumber(auditReader);

//then you use the auditReader :-)
CustomRevisionEntity cRevEntity = auditReader.findRevision(
                CustomRevisionEntity.class, revisionNumber );

//Then you can just get your Username
String userName = cRevEntity.getUsername();

这是方法签名

 /**
 * A helper method; should be used only if a custom revision entity is used. See also {@link RevisionEntity}.
 * @param revisionEntityClass Class of the revision entity. Should be annotated with {@link RevisionEntity}.
 * @param revision Number of the revision for which to get the data.
 * @return Entity containing data for the given revision.
 * @throws IllegalArgumentException If revision is less or equal to 0 or if the class of the revision entity
 * is invalid.
 * @throws RevisionDoesNotExistException If the revision does not exist.
 * @throws IllegalStateException If the associated entity manager is closed.
 */
<T> T findRevision(Class<T> revisionEntityClass, Number revision) throws IllegalArgumentException,
        RevisionDoesNotExistException, IllegalStateException;

在 Hibernate-envers 3.6.3.Final 源代码中,这是在 AuditReaderImpl.java 第 193 行实现的:

@SuppressWarnings({"unchecked"})
public <T> T findRevision(Class<T> revisionEntityClass, Number revision) throws IllegalArgumentException,
        RevisionDoesNotExistException, IllegalStateException {
    checkNotNull(revision, "Entity revision");
    checkPositive(revision, "Entity revision");
    checkSession();

    Set<Number> revisions = new HashSet<Number>(1);
    revisions.add(revision);
    Query query = verCfg.getRevisionInfoQueryCreator().getRevisionsQuery(session, revisions);

    try {
        T revisionData = (T) query.uniqueResult();

        if (revisionData == null) {
            throw new RevisionDoesNotExistException(revision);
        }

        return revisionData;
    } catch (NonUniqueResultException e) {
        throw new AuditException(e);
    }
}

更新 - 缺少 Spring 配置

查看堆栈跟踪,您缺少一些 spring 事务配置。 使用声明性配置或使用注释。

声明式配置

您需要在配置 xml 中声明事务的使用,这是使用 AOP 切入点完成的。看着这个example ,你可以看到它首先设置了 TransactionManager 和它的 DataSource,然后声明 x.y.service.FooService 的每个方法都需要一个事务

<!-- ensure that the above transactional advice runs for any execution
     of an operation defined by the FooService interface -->
<aop:config>
    <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>

您提供的配置缺少 AOP 配置。为方便起见,您可以将特定包中的每个类配置为使用事务。

<aop:config>
    <aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/>
</aop:config>

看到这只是 expression="execution(* x.y.service.*.*(..))" 发生了变化。

使用@Transactional

值得庆幸的是,Spring 提供了一种更简单的方法来声明 @Transactional 方法的使用,只需将类、接口(interface)或方法注释为 @Transactional

// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);
}

之后我们需要配置 spring 来扫描我们的代码以查找 @Transactional,以便在需要时生成适当的代理 beans 实例。

<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager"/><!-- a PlatformTransactionManager is still required -->

这是 complete example对于@Transactional 和我的 reference用于有关事务的 spring 配置。

在 Hibernate 3 上启用配置

Hibernate 3 需要特殊配置才能与 Envers 一起使用,您需要在 persistence.xml 中添加它。 Example

<property name="hibernate.ejb.event.post-insert" value="org.hibernate.ejb.event.EJB3PostInsertEventListener,org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.post-update" value="org.hibernate.ejb.event.EJB3PostUpdateEventListener,org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.post-delete" value="org.hibernate.ejb.event.EJB3PostDeleteEventListener,org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.pre-collection-update" value="org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.pre-collection-remove" value="org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.post-collection-recreate" value="org.hibernate.envers.event.AuditEventListener" />

如果您没有 persistence.xml 或 hibernate.cfg.xml,并且您声明了 SessionFactory 并且它正常工作,您需要编辑您的 spring 配置,例如 this

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    ....
    <property name="eventListeners">
        <map>
            <entry key="post-insert" value-ref="auditListener"/>
            <entry key="post-update" value-ref="auditListener"/>
            <entry key="post-delete" value-ref="auditListener"/>
            <entry key="pre-collection-update" value-ref="auditListener"/>
            <entry key="pre-collection-remove" value-ref="auditListener"/>
            <entry key="post-collection-recreate" value-ref="auditListener"/>
        </map>
    </property>
    ...
</bean>

<bean id="auditListener" class="org.hibernate.envers.event.AuditEventListener"/>

关于java - 如何从 customRevisionEntity 检索用户名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30273925/

有关java - 如何从 customRevisionEntity 检索用户名的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  5. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  6. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  7. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  8. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  9. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  10. ruby - 如何使用文字标量样式在 YAML 中转储字符串? - 2

    我有一大串格式化数据(例如JSON),我想使用Psychinruby​​同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解

随机推荐