草庐IT

java - 如何使用 TestNg 对 Hibernate、H2 进行测试?

coder 2024-03-21 原文

什么是测试 DAO 层的正确方法?

我在创建和销毁 SessionFactory 时使用了 @BeforeMethod@AfterMethod 注释方法,但它不适用于多个测试。如果测试一个接一个地运行,但当它们一起运行时没有使用 maven 构建,所以我决定我应该使用 TestNg 组对它们进行分组并执行 @BeforeGroup@AfterGroup 方法,其中我对 Hibernate 做了同样的事情。

所以我做了这样的事情:

@Test(groups = {"integration"})
public class IntegrationTest
{
    protected SessionFactory sessionFactory;

    @BeforeGroups(groups = {"integration"})
    public void setUpHibernate() throws Exception
    {
        // here I configure sessionFactory
        this.sessionFactory = ...
    }

    @AfterGroups(groups = {"integration"})
    public void putItDown() throws Exception
    {
        sessionFactory.close();
    }
}

我的每个测试都像这样扩展了这个类

@Test(groups = "integration")
public class RateRepositoryHibernateTest  extends IntegrationTest
{
    ...
}

然后我注意到只有一个扩展测试设置了 sessionFactory,其余的都是 null,这并不奇怪,因为该方法应该运行一次。现在我真的不知道该怎么办。

如何围绕组测试方法从@BeforeGroup 方法传递数据?

如何以不同的方式做到这一点?

如何在每次测试之前和之后设置和拆除 SessionFactory,但在多次测试中我不会得到任何悲观的锁定异常?

-- 编辑:

也欢迎回答解释如何以不同的方式做到这一点。我想知道使用 TestNg、Hibernate 和一些内存数据库进行集成测试的最新技术。

-- 编辑 2:

堆栈跟踪,下面的测试代码

Hibernate: select roomtype_.type_name from ROOM_TYPES roomtype_ where roomtype_.type_name=?
Hibernate: call next value for rates_sequence
Hibernate: call next value for rates_sequence
Hibernate: insert into ROOMS (prefix, housekeepingStatus, availability, maxExtraBeds, standard, maximum, type, name) values (?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into RATES (standardPrice, upchargeExtraPerson, upchargeExtraBed, room, RATE_TYPE, id) values (?, ?, ?, ?, 'R', ?)
Hibernate: insert into RATES (standardPrice, upchargeExtraPerson, upchargeExtraBed, room, seasonId, RATE_TYPE, id) values (?, ?, ?, ?, ?, 'S', ?)
Hibernate: insert into ROOM_TYPES (type_name) values (?)
Hibernate: select roomtype_.type_name from ROOM_TYPES roomtype_ where roomtype_.type_name=?
Hibernate: call next value for rates_sequence
Hibernate: insert into ROOMS (prefix, housekeepingStatus, availability, maxExtraBeds, standard, maximum, type, name) values (?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into RATES (standardPrice, upchargeExtraPerson, upchargeExtraBed, room, RATE_TYPE, id) values (?, ?, ?, ?, 'R', ?)
org.hibernate.PessimisticLockException: Timeout trying to lock table ; SQL statement:
insert into RATES (standardPrice, upchargeExtraPerson, upchargeExtraBed, room, RATE_TYPE, id) values (?, ?, ?, ?, 'R', ?) [50200-168]
    at org.hibernate.dialect.H2Dialect$2.convert(H2Dialect.java:317)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
    at org.hibernate.engine.jdbc.internal.proxy.AbstractStatementProxyHandler.continueInvocation(AbstractStatementProxyHandler.java:129)
    at org.hibernate.engine.jdbc.internal.proxy.AbstractProxyHandler.invoke(AbstractProxyHandler.java:81)
    at $Proxy13.executeUpdate(Unknown Source)
    at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:56)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2962)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3403)
    at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:88)
    at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:362)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:354)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:275)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:326)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1210)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:399)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
    at net.mklew.hotelms.persistance.RatesPersistanceTest.should_save_rates_and_retrieve_them_with_success(RatesPersistanceTest.java:80)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:80)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:673)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:842)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1166)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
    at org.testng.TestRunner.runWorkers(TestRunner.java:1178)
    at org.testng.TestRunner.privateRun(TestRunner.java:757)
    at org.testng.TestRunner.run(TestRunner.java:608)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:334)
    at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:329)
    at org.testng.SuiteRunner.privateRun(SuiteRunner.java:291)
    at org.testng.SuiteRunner.run(SuiteRunner.java:240)
    at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
    at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
    at org.testng.TestNG.runSuitesSequentially(TestNG.java:1158)
    at org.testng.TestNG.runSuitesLocally(TestNG.java:1083)
    at org.testng.TestNG.run(TestNG.java:999)
    at org.testng.remote.RemoteTestNG.run(RemoteTestNG.java:111)
    at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:203)
    at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:174)
    at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:111)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.h2.jdbc.JdbcSQLException: Timeout trying to lock table ; SQL statement:
insert into RATES (standardPrice, upchargeExtraPerson, upchargeExtraBed, room, RATE_TYPE, id) values (?, ?, ?, ?, 'R', ?) [50200-168]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
    at org.h2.message.DbException.get(DbException.java:158)
    at org.h2.command.Command.filterConcurrentUpdate(Command.java:276)
    at org.h2.command.Command.executeUpdate(Command.java:232)
    at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:156)
    at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:142)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.hibernate.engine.jdbc.internal.proxy.AbstractStatementProxyHandler.continueInvocation(AbstractStatementProxyHandler.java:122)
    ... 47 more
Caused by: org.h2.jdbc.JdbcSQLException: Concurrent update in table "PRIMARY_KEY_4": another transaction has updated or deleted the same row [90131-168]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
    at org.h2.message.DbException.get(DbException.java:169)
    at org.h2.message.DbException.get(DbException.java:146)
    at org.h2.table.RegularTable.addRow(RegularTable.java:146)
    at org.h2.command.dml.Insert.insertRows(Insert.java:124)
    at org.h2.command.dml.Insert.update(Insert.java:84)
    at org.h2.command.CommandContainer.update(CommandContainer.java:75)
    at org.h2.command.Command.executeUpdate(Command.java:230)
    ... 54 more

测试代码:

public class RatesPersistanceTest
{

    protected SessionFactory sessionFactory;
    protected HibernateSessionFactory hibernateSessionFactory;

    @BeforeMethod
    public void setUpHibernate() throws Exception
    {
        Logger logger = mock(Logger.class);
        NativelyConfiguredHibernateSessionFactory hibernateSessionFactory = new
                NativelyConfiguredHibernateSessionFactory(logger);
        this.sessionFactory = hibernateSessionFactory.getSessionFactory();
        this.hibernateSessionFactory = hibernateSessionFactory;
    }

    @AfterMethod
    public void putItDown() throws Exception
    {
        sessionFactory.close();
    }

    @Test
    public void should_save_rates_and_retrieve_them_with_success()
    {
        // given
        Money standardPrice = Money.parse("USD 85");
        Money upchargeExtraPerson = Money.parse("USD 80");
        Money upchargeExtraBed = Money.parse("USD 75");
        RoomType roomType = getMeRoomType();
        Room room = getMeRoom(roomType);

        AvailabilityPeriod availabilityPeriod = new AvailabilityPeriod(DateTime.now(), DateTime.now().plusDays(5),
                true);
        Season season = new BasicSeason("season name", availabilityPeriod);
        Rate seasonRate = new SeasonRate(standardPrice, upchargeExtraPerson, upchargeExtraBed, room, season);

        Session session = sessionFactory.openSession();
        session.beginTransaction();

        session.save(roomType);
//        session.save(room);

        session.getTransaction().commit();
        session.close();


        session = sessionFactory.openSession();
        session.beginTransaction();

        //        session.save(roomType);
        session.save(room);

        session.getTransaction().commit();
        session.close();

        session = sessionFactory.openSession();
        session.beginTransaction();
        session.save(season);

        session.save(seasonRate);

        session.getTransaction().commit();
        session.close();
        // when
        session = sessionFactory.openSession();
        session.beginTransaction();
        final List<Rate> list = session.createQuery("from Rate").list();

        // then
        assertThat(list).contains(seasonRate);
        session.getTransaction().commit();
        session.close();

    }

    @Test( expectedExceptions = org.hibernate.exception.ConstraintViolationException.class)
    public void season_rate_should_violate_db_constraints_when_saved_without_season()
    {
        // given
        Money standardPrice = Money.parse("USD 85");
        Money upchargeExtraPerson = Money.parse("USD 80");
        Money upchargeExtraBed = Money.parse("USD 75");
        RoomType roomType = getMeRoomType();
        final RoomName roomName = new RoomName("103");

        final Money roomStandardPrice = Money.parse("USD 100");
        final Money roomUpchargeExtraPerson = Money.parse("USD 50");
        final Money roomUpchargeExtraBed = Money.parse("USD 20");
        final RackRate rackRate = new RackRate(roomStandardPrice, roomUpchargeExtraPerson, roomUpchargeExtraBed, null);
        final int maxExtraBeds = 2;
        final Occupancy occupancy = new Occupancy(4, 2);
        Room room = new Room("C", roomName, roomType, HousekeepingStatus.CLEAN, RoomAvailability.AVAILABLE,
                maxExtraBeds, occupancy, standardPrice, upchargeExtraPerson, upchargeExtraBed);


        AvailabilityPeriod availabilityPeriod = new AvailabilityPeriod(DateTime.now(), DateTime.now().plusDays(5),
                true);
        Season season = new BasicSeason("season name", availabilityPeriod);
        Rate seasonRate = new SeasonRate(standardPrice, upchargeExtraPerson, upchargeExtraBed, room, null);

        Session session = sessionFactory.openSession();
        session.beginTransaction();

        session.save(room);
        // when
        session.save(seasonRate);

        session.getTransaction().commit();
        session.close();
        // then exception should be thrown
    }

//    @Test
//    public void package_rate_should_violate_db_constraints_when_saved_without_package()
//    {
//
//    }

    private RoomType getMeRoomType()
    {
        final RoomType roomType = new RoomType("cheap" + DateTime.now().toString());
        return roomType;
    }

    private Room getMeRoom(RoomType roomType)
    {
        final RoomName roomName = new RoomName("101001" + DateTime.now().toString());

        final Money standardPrice = Money.parse("USD 100");
        final Money upchargeExtraPerson = Money.parse("USD 50");
        final Money upchargeExtraBed = Money.parse("USD 20");
        final RackRate rackRate = new RackRate(standardPrice, upchargeExtraPerson, upchargeExtraBed, null);
        final int maxExtraBeds = 2;
        final Occupancy occupancy = new Occupancy(4, 2);
        return new Room("C", roomName, roomType, HousekeepingStatus.CLEAN, RoomAvailability.AVAILABLE, maxExtraBeds,
                occupancy, standardPrice, upchargeExtraPerson, upchargeExtraBed);
    }

}

hibernate 配置:

<hibernate-configuration>

    <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">org.h2.Driver</property>
        <property name="connection.url">jdbc:h2:mem:db1;DB_CLOSE_DELAY=0;MVCC=true</property>
        <property name="connection.username">sa</property>
        <property name="connection.password"/>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.H2Dialect</property>

        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">org.hibernate.cache.internal.NoCachingRegionFactory</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">create-drop</property>

       <!-- skipped mappings -->

    </session-factory>

</hibernate-configuration>

最佳答案

测试使用 Hibernate 的代码的首选方法是使用 @BeforeMethod@AfterMethod 测试方法。我没有任何使用 TestNg 的经验,但我认为它处理这些函数的方式与 JUnit3.8.1 处理 setUp() 和 tearDown() 的方式类似,这正是我所使用的。

对我来说关键点是 the order of unit test execution shouldn't matter .如果你正在使用 @BeforeGroup@AfterGroup,那么你有相同的 SessionFactory 实例,因此有相同的数据库(在内存中或不在内存中) ),并且测试在该 SessionFactory 上运行的任何变异操作都会影响后面执行读取操作的测试。这可能是需要的(尽管顺序可能必须是可预测的),在这种情况下,您实际上是在谈论单个“集成测试”,但对于您的情况,听起来您希望测试是独立的。

下一个问题是如何做到这一点。我所做的是配置 Hibernate 以创建并连接到内存数据库,在其上运行创建模式脚本,并为每个测试方法创建一个 SessionFactory。

@Override
protected void setUp() throws Exception {
    super.setUp();
    String dialectClassName = HSQLDialect.class.getName();
    AnnotationConfiguration config = new AnnotationConfiguration().addAnnotatedClass(DividendScheduleGeneratorImpl.class);

    config.setProperty(Environment.DIALECT, dialectClassName);
    config.setProperty(Environment.DRIVER, jdbcDriver.class.getName());
    config.setProperty(Environment.URL, "jdbc:hsqldb:mem:testDB");
    config.setProperty(Environment.USER, "SA");
    config.setProperty(Environment.PASS, "");

    SchemaExport export = new SchemaExport(config);
    export.create(false, true);

    sessions = config.buildSessionFactory();
}

@Override
protected void tearDown() throws Exception {
    sessions.close();
    sessions = null;

    super.tearDown();
}

注意事项:

  1. 模式创建并不是那么快,至少对于我使用的 HSQLDB 而言是这样。但是,只要您清楚地将真正测试 Hibernate 的测试与使用可模拟持久层的测试分开,您应该没问题,因为不会有那么多测试必须使用实际数据库。
  2. 我的测试是单线程运行的。如果您正在运行多线程,则需要设置数据库名称,以便它对于并发运行的测试是唯一的。

编辑:

根据您的错误堆栈跟踪和代码,以及测试独立运行的事实,我怀疑 PessimisticLockExceptions 是由针对同一数据库在不同线程上运行的测试引起的。可能您甚至在 RATES 表上有一个以数据库为中心的死锁。处理它的两个选项是让测试生成独立的数据库,或者告诉 TestNG 连续运行测试。

首先,您必须以编程方式编辑来自

的 Hibernate 连接字符串
jdbc:h2:mem:db1;DB_CLOSE_DELAY=0;MVCC=true

jdbc:h2:mem:<TESTNAME>;DB_CLOSE_DELAY=0;MVCC=true

或类似的。在 JUnit 中,我会使用 TestCase.getName() ,我假设 TestNG 中有类似的可用功能。

另一种方法是串行运行。根据 TestNG 文档,序列化测试应该可以通过注释类(而不是方法)来添加 @Test(singleThreaded=true)

@Test(singleThreaded=true)
public class RatesPersistanceTest

至少它应该是这样工作的,也许当你尝试它时你注释了方法?

关于java - 如何使用 TestNg 对 Hibernate、H2 进行测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14060632/

有关java - 如何使用 TestNg 对 Hibernate、H2 进行测试?的更多相关文章

  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. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  4. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  5. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  6. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  7. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

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

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

  9. 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

  10. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

随机推荐