使用Spring对Service层进行测试,无法查询到刚刚插入的记录?

如题,本人使用Spring提供的:AbstractTransactionalDataSourceSpringContextTests类对Service层进行测试。测试的方法是向数据库中插入一条用户记录,然后再重新检索出来。代码如下:

【1】DAO层代码:
[code="java"]
public User saveUser(User newUser) {
getHibernateTemplate().save(newUser);
//getHibernateTemplate().flush();
return (User) getHibernateTemplate().get(User.class,
newUser.getUserId());
}
[/code]

【2】Service层代码:
[code="java"]public User saveUser(User newUser) throws NewsletterException {
try {
return userDao.saveUser(newUser);
} catch (HibernateObjectRetrievalFailureException he) {
throw new ServiceException(systemMessage.getMessage(SERVICE_FAILED)
+ ": saveUser");
} catch (Exception e) {
throw new NewsletterException(e.getMessage());
}
}[/code]

[code="java"]public User getUserByName(String userName) throws NewsletterException {
try {
return userDao.getUserByName(userName);
} catch (HibernateObjectRetrievalFailureException he) {
throw new ServiceException(systemMessage.getMessage(SERVICE_FAILED)
+ ": userName");
} catch (Exception e) {
throw new NewsletterException(e.getMessage());
}
}[/code]

【3】测试代码:
[code="java"]public void testSaveUser() {
User newUser = createUser();
try {
userService.saveUser(newUser);
assertTrue(true);

String sql = "Select USERNAME from NEWSLETTER_USER where UPPER(USERNAME)= ?";
String userName = (String) jdbcTemplate.queryForObject(sql,
new Object[] { "PAUL" }, java.lang.String.class);
assertNotNull(userName);
assertEquals(userName, newUser.getUserName());

} catch (NewsletterException ne) {
fail(ne.getMessage());
[/code] }
}

【4】Spring配置文件:
[code="xml"]

<bean id="userServiceTarget"
    class="com.newsletter.service.impl.UserServiceImpl">
    <property name="userDao">
        <ref local="userDao" />
    </property>
    <property name="systemMessage">
        <ref local="systemMessage" />
    </property>
</bean>

<!-- Transactional proxy for the User Service -->
<bean id="userService"
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager">
        <ref local="transactionManager" />
    </property>
    <property name="target">
        <ref local="userServiceTarget" />
    </property>
    <property name="transactionAttributes">
        <props>
            <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
            <prop key="save*">PROPAGATION_REQUIRED</prop>
            <prop key="update*">PROPAGATION_REQUIRED</prop>
            <prop key="delete*">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
</bean>

[/code]

【5】测试问题:

[color=red]1. 使用上面的测试代码,发现jdbcTemplate.queryForObject方法获取到的结果为空,测试失败。

  1. 在testSaveUser方法末尾,即便加上setComplete()方法,发现数据已经插入,但query方法同样返回NULL

  2. 假如我使用上面Service层的userService.getUserByName方法进行测试,测试通过

  3. 假如我在DAO层的saveUser方法里面加上getHibernateTemplate().flush();测试也通过[/color]

[b]请问为什么即便我在测试方法中加上setComplete(),而且数据库中查到了记录,但每次都是返回NULL是什么原因?[/b]
[b]问题补充:[/b]
谢谢llade,假如我调用了setComplete()方法,Spring确实是提交了事务的,因为我在数据库中已经查询到相应的记录。

我测试过3种情况的setComplete(),第一种是在saveUser调用之后,第二种是在queryForObject之后,第三种是分别在saveUser和queryForObject之后。

但每次都是同样的结果。

需要将hibernate的session,flush一下
[code="java"]SessionFactoryUtils.getSession(getSessionFactory(), false).flush()[/code]
这样你刚才做的操作才会体现在实际的数据库表中。然后通过jdbc就能查到了。

[code="java"]
public void testSaveUser() {

User newUser = createUser();

try {

userService.saveUser(newUser);

[b]SessionFactoryUtils.getSession(getSessionFactory(), false).flush();[/b]
assertTrue(true);

String sql = "Select USERNAME from NEWSLETTER_USER where UPPER(USERNAME)= ?";

String userName = (String) jdbcTemplate.queryForObject(sql,

new Object[] { "PAUL" }, java.lang.String.class);

assertNotNull(userName);

assertEquals(userName, newUser.getUserName());

} catch (NewsletterException ne) {

fail(ne.getMessage());
}
}

[/code]

有意思的问题。在这种情况下还是先看看到底Connection有没有commit吧。
方法是,做一个代理类,看看connecton有没提交。

[code="java"]
package llade.test.jdbc

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.Map;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class MyDataSource extends BasicDataSource {
Log logger = LogFactory.getLog(MyDataSource.class);
class ProxyConnection implements Connection{

    Connection innnerConnection;
    String uuid;

    public ProxyConnection(Connection conn,String uuid){
        this.innnerConnection=conn;
        this.uuid=uuid;
    }

    public void clearWarnings() throws SQLException {
        this.innnerConnection.clearWarnings();
    }

    public void close() throws SQLException {
        this.innnerConnection.close();
    }

    public void commit() throws SQLException {
        //正是我们想要知道是否调用了commit();
                       //如果信不过logger可以用System.out
        logger.debug("connection:"+this.uuid+" commit");
        this.innnerConnection.commit();
    }

    public Statement createStatement() throws SQLException {
        return this.innnerConnection.createStatement();
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        return this.innnerConnection.createStatement(resultSetType, resultSetConcurrency);
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return this.innnerConnection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    public boolean getAutoCommit() throws SQLException {
        return this.innnerConnection.getAutoCommit();
    }

    public String getCatalog() throws SQLException {
        return this.innnerConnection.getCatalog();
    }

    public int getHoldability() throws SQLException {
        return this.innnerConnection.getHoldability();
    }

    public DatabaseMetaData getMetaData() throws SQLException {
        return this.innnerConnection.getMetaData();
    }

    public int getTransactionIsolation() throws SQLException {
        return this.innnerConnection.getTransactionIsolation();
    }

    public Map<String, Class<?>> getTypeMap() throws SQLException {
        return this.innnerConnection.getTypeMap();
    }

    public SQLWarning getWarnings() throws SQLException {
        return this.innnerConnection.getWarnings();
    }

    public boolean isClosed() throws SQLException {
        return this.innnerConnection.isClosed();
    }

    public boolean isReadOnly() throws SQLException {
        return this.innnerConnection.isReadOnly();
    }

    public String nativeSQL(String sql) throws SQLException {
        return this.innnerConnection.nativeSQL(sql);
    }

    public CallableStatement prepareCall(String sql) throws SQLException {
        return this.innnerConnection.prepareCall(sql);
    }

    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return this.innnerConnection.prepareCall(sql, resultSetType, resultSetConcurrency);
    }

    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return this.innnerConnection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return this.innnerConnection.prepareStatement(sql);
    }

    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
        return this.innnerConnection.prepareStatement(sql, autoGeneratedKeys);
    }

    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
        return this.innnerConnection.prepareStatement(sql, columnIndexes);
    }

    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
        return this.innnerConnection.prepareStatement(sql, columnNames);
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return this.innnerConnection.prepareStatement(sql, resultSetType, resultSetConcurrency);
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return this.innnerConnection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        this.innnerConnection.releaseSavepoint(savepoint);
    }

    public void rollback() throws SQLException {
        this.innnerConnection.rollback();
    }

    public void rollback(Savepoint savepoint) throws SQLException {
        this.innnerConnection.rollback(savepoint);
    }

    public void setAutoCommit(boolean autoCommit) throws SQLException {
        this.innnerConnection.setAutoCommit(autoCommit);
    }

    public void setCatalog(String catalog) throws SQLException {
        this.innnerConnection.setCatalog(catalog);
    }

    public void setHoldability(int holdability) throws SQLException {
        this.innnerConnection.setHoldability(holdability);
    }

    public void setReadOnly(boolean readOnly) throws SQLException {
        this.innnerConnection.setReadOnly(readOnly);
    }

    public Savepoint setSavepoint() throws SQLException {
        return this.innnerConnection.setSavepoint();
    }

    public Savepoint setSavepoint(String name) throws SQLException {
        return this.innnerConnection.setSavepoint(name);
    }

    public void setTransactionIsolation(int level) throws SQLException {
        this.innnerConnection.setTransactionIsolation(level);
    }

    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
        this.innnerConnection.setTypeMap(map);
    }

}

@Override
public Connection getConnection() throws SQLException {

    Connection conn=super.getConnection();
     return new ProxyConnection(conn,java.util.UUID.randomUUID().toString());
}

@Override
public Connection getConnection(String user, String password) throws SQLException {
    Connection conn=super.getConnection(user,password);
    if(conn instanceof ProxyConnection)return conn;
    return new ProxyConnection(conn,java.util.UUID.randomUUID().toString());
}

}

[/code]

那是因为, Hibernate和JDBC不在一个事务内.

所以,
[1] 你把Hibernate的Session Flush后, JDBC就可以查到了.

[2] 你不用JDBC去查,继续使用Hibernate去查询,当然还可以查到.

最近一直在看Hibernate和JDBC的框架整合,就一直遇到同类的问题.