“只读模式下不允许写入操作”错误:与 Spring @Service @transaction @Repository 和 Hibernate 混淆

Flo*_*000 4 java spring hibernate transactions

我正在使用 Spring 和 Hibernate 开发一个现有项目,并且很困惑,因为我得到了一个

org.springframework.dao.InvalidDataAccessApiUsageException:只读模式下不允许写入操作(FlushMode.MANUAL):将会话转换为FlushMode.COMMIT/AUTO或从事务定义中删除“readOnly”标记。

尝试保存对象时出错,但我仍然找不到到底出了什么问题。

有一个使用 注释的服务层@Servicesave一个应该是事务性的方法,因此它使用 注释@Transactional(readOnly = false)。对我来说,这意味着 spring 应该自己处理事务。

@Service
public class LadyService {
    Logger log = Logger.getLogger(LadyService.class);
    @Autowired
    private PictureDAO pictureDao;
    @Autowired
    private LadyDAO ladyDao;
    @Autowired
    private AddressDAO addressDao;

    @Transactional(readOnly = false)
    public void save(Lady lady) {
        Address a = addressDao.getExistingAddress(lady.getAddress());
        if (a == null) {
            a = addressDao.save(lady.getAddress());
        }
        lady.setAddress(a);
        ladyDao.save(lady);
        pictureDao.savePictures(lady.getPictures());
    }
Run Code Online (Sandbox Code Playgroud)

在进行保存时会发生错误AddressDAO。它被注释为@Repository.

@Repository
public class AddressDAO extends HibernateDaoSupport {

    public Address save(Address address) {
        getHibernateTemplate().save(address);  <-- write not permitted error happens here
        return address;
    }

    @SuppressWarnings({ "unchecked" })
    public Address getExistingAddress(Address address) {
        DetachedCriteria cd = DetachedCriteria.forClass(Address.class);
        cd.add(Restrictions.eqOrIsNull("administrative_area_level_1",
                address.getAdministrative_area_level_1()));
        cd.add(Restrictions.eqOrIsNull("administrative_area_level_2",
                address.getAdministrative_area_level_2()));
        List<Address> result = (List<Address>) getHibernateTemplate()
                .findByCriteria(cd);

        if (result.isEmpty()) {
            return null;
        } else {
            return (Address) result.get(0);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为会发生的情况是,@Transactional让 spring 创建一个会话和一个事务用于在服务层上进行保存,并且在 DAO 中,hibernate 模板将获取 spring 管理的当前会话和事务并使用它来保存对象。

不过,错误消息让我认为我的 service 方法和 dao 方法不在同一个事务中。

在 servlet-context.xml 中有以下语句:

<annotation-driven />

<context:component-scan base-package="com.kog.fable" />

<beans:bean id="mySessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <beans:property name="dataSource" ref="myDataSource" />
    <beans:property name="packagesToScan">
        <beans:array>
            <beans:value>com.kog.fable.**.*</beans:value>
        </beans:array>
    </beans:property>
    <beans:property name="hibernateProperties">
        <beans:props>
            <beans:prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect
            </beans:prop>
            <!-- create, validate, update -->
            <beans:prop key="hibernate.hbm2ddl.auto">create</beans:prop>
            <beans:prop key="hibernate.show_sql">false</beans:prop>
            <beans:prop key="hibernate.connection.pool_size">10</beans:prop>
            <beans:prop key="hibernate.connection.autocommit ">false</beans:prop>
        </beans:props>
    </beans:property>
</beans:bean>

<tx:annotation-driven transaction-manager="transactionManager" />

<beans:bean id="transactionManager"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <beans:property name="sessionFactory" ref="mySessionFactory" />
</beans:bean>

<beans:bean id="addressDAO" class="com.kog.fable.dao.AddressDAO">
    <beans:property name="sessionFactory" ref="mySessionFactory" />
</beans:bean>

<beans:bean id="ladyDAO" class="com.kog.fable.dao.LadyDAO">
    <beans:property name="sessionFactory" ref="mySessionFactory" />
</beans:bean>

<beans:bean id="pictureDAO" class="com.kog.fable.dao.PictureDAO">
    <beans:property name="sessionFactory" ref="mySessionFactory" />
</beans:bean>
Run Code Online (Sandbox Code Playgroud)

这里我不明白为什么,如果使用组件扫描,DAO bean 仍然显式声明。既然 DAO 类都用 ​​注释,那么组件扫描功能是否应该能够自行创建这些组件@Repository?由于我认为此配置可能会创建重复的 bean,因此我尝试删除 xml 条目,但随后我开始得到:

org.springframework.beans.factory.BeanCreationException:创建名称为“addressController”的bean时出错:自动装配依赖项注入失败;嵌套异常是 org.springframework.beans.factory.BeanCreationException:无法自动装配字段:私有 com.kog.fable.dao.AddressDAO com.kog.fable.controller.AddressController.addressDAO; 嵌套异常是 org.springframework.beans.factory.BeanCreationException:创建文件 [***\com\kog\fable\dao\AddressDAO.class] 中定义的名为“addressDAO”的 bean 时出错:调用 init 方法失败;嵌套异常是 java.lang.IllegalArgumentException:需要 'sessionFactory' 或 'hibernateTemplate'

在这里,我认为为我的 DAO 扩展 HibernateDaoSupport 会使它们继承 sessionFactory 和相关方法,所以我不明白会发生什么。

我读过,我可以将刷新模式设置为 AUTO 或将模板上的 setCheckWriteOperations 设置为 FALSE 来解决此类问题,并且它似乎有效,但我想这并不能像我一样确保所有情况下的事务一致性喜欢它。

任何帮助将不胜感激,因为我对 Spring 和 Hibernate 很陌生,并且有点卡在这里。

M. *_*num 5

当扩展时,HibernateDaoSupport您不会从自动装配中受益,您必须重写该setSessionFactory方法并@Autowired在其上添加注释。不然就不行了。

我还期望<tx:annotation-driven />没有它@Transactional几乎没有用并且不会做任何事情。