6

Can someone explain why the "lastAccessed" date does not get saved to the database in this example and how I can get it to save to the DB? My understanding is that the do object is an attached object after the save() call and therefore all modifications should be persisted automatically.

Note: "myDate" is persisted correctly, so all other spring configuration seems to be correct.

@Transactional(readOnly = false)
public DateObject getOrCreateDateObject(Date myDate) {
    DateObject do = null;

    do = getCurrentDateObject();  // For my tests, this has been returning null

    if (do == null) {
        // create a new object
        do = new DateObject();
        do.setDate(myDate);
        sessionFactory.getCurrentSession().save(do);
    }

    // This does not persist to the database
    do.setLastAccessed(new Date());

    return do;
}

I have also tried some of the following combinations (and more) after the save() call. None of these work:

sessionFactory.getCurrentSession().merge(do);  // tried before and after do.setDate(d2)

sessionFactory.getCurrentSession().update(do);

sessionFactory.getCurrentSession().saveOrUpdate(do);

sessionFactory.getCurrentSession().flush();

DateObject doCopy = (DateObject)sessionFactory.getCurrentSession().load(DateObject.class, do.getId());
sessionFactory.getCurrentSession().merge(doCopy);
doCopy.setLastAccessed(new Date());

I'm hoping this is an easy answer that I'm just not seeing. Thank you for your help!

Edit #1 05/22/2012

As requested, here is the mapping for this entity, specified in src/main/resources/META-INF/dateobject.hbm.xml. I can see that the columns are created in the database using "SELECT * FROM dateObjects" in the mysql client. MY_DATE is populated correctly, but LAST_ACCESSED is set to NULL.

<class name="com.example.entity.DateObject" table="dateObjects">
    <id name="id" column="DATE_OBJECT_ID">
        <generator class="identity" />
    </id>
    <property name="date" type="date" column="MY_DATE" />
    <property name="lastAccessed" type="date" column="LAST_ACCESSED" />
</class>

Edit #2 05/24/2012

I have a working SSCCE at https://github.com/eschmidt/dateobject. The interesting thing is that the web client (calling localhost:8080/view/test) shows that lastAccessed is set correctly, but when I check the database with the MySQL client, it shows that lastAccessed is NULL. With this complete set of code, can anybody see why the database wouldn't update even though the method is marked @Transactional?

Eric
  • 63
  • 1
  • 5

2 Answers2

2

If you're absolutely certain that after running that code, do.date is stored in the db and do.lastAccessed isn't, then your connection and transaction are obviously set up correctly. My first guess would be incorrect mappings, since that's the simplest solution. You don't happen to have an @Transient on the field, the getter, or the setter for lastAccessed, do you? (Assuming, of course, that you're using annotations to map your domain objects.)

If you could provide an SSCCE, I'll bet I or someone else can give you a definitive answer.

Update: It's hard trimming a full application down to the smallest possible code that demonstrates a problem. The upshot is that you'll likely find the answer while you're at it. I have lots of sample projects in github that might help guide you if you just need a few nudges in the right direction. basic-springmvc might be closest to what you're doing, but it uses annotations instead of xml for mappings. It's also a Spring MVC project. It's a lot simpler to start a Spring context manually in a main class than to worry about a whole servlet container and the multiple contexts that Spring MVC wants you to have. spring-method-caching, for one, has an example of doing that.

As for the mapping you posted, it looks fine, though it's been a long while since I touched an XML mapping. Are you using field or property access? That could possibly have a bearing on things. Also, are there any custom listeners or interceptors in the SessionFactory that might be twiddling with your objects?

Ryan Stewart
  • 126,015
  • 21
  • 180
  • 199
  • He never merged/persisted/saved the object after modifying the in-memory copy. What does it have to do with the connection or tx? – ianpojman May 23 '12 at 03:09
  • 1
    As I've explained in my comment to your answer, you're just not getting how Hibernate works. It *doesn't matter* when you call save(). – Ryan Stewart May 23 '12 at 03:26
  • @RyanStewart: I added my hibernate mapping to the question. I am certain that do.date is stored and do.lastAccessed is still NULL. There is no Transient annotation on the field or the [gs]etter. I will work on providing an SSCCE, but that may be difficult as this is an enterprise spring application. Do you have any tricks on setting up an SSCCE for spring? Thanks! – Eric May 23 '12 at 03:50
  • @RyanStewart: I've started to set up an SSCCE here: https://github.com/eschmidt/dateobject However, I when I try to deploy this application to Glassfishv3, I get the following exception: Exception while invoking class com.sun.enterprise.web.WebApplication start method : java.lang.Exception: java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: java.lang.IllegalArgumentException: java.lang.ClassNotFoundException: org.spring.framework.web.context.ContextLoaderListener Do you know why it can't seem to find the spring-web jar? – Eric May 23 '12 at 16:08
  • Nothing wrong with the container. It's the wrong class name. `org.springframework`, not `org.spring.framework`. – Ryan Stewart May 23 '12 at 19:00
  • @RyanStewart: Wow. An extra pair of eyes really helps, thanks! Now I can get back to focusing on the real issue... – Eric May 23 '12 at 19:46
  • @RyanStewart: Ok, I have a complete SSCCE ready at https://github.com/eschmidt/dateobject. I also updated my original question with new findings. See the edit above. – Eric May 25 '12 at 03:34
  • You've made the unfortunate mistake of committing your output directory along with all the dependent jars and packaged artifacts, making your repository ridiculously huge and therefore difficult to clone. There also doesn't seem to be a way to actually run the project if I were to clone the dozens (hundreds?) of MB of it. The ["Self Contained"](http://sscce.org/#selfcon) bit is supposed to mean that any Joe Schmoe can pick it up and run it to see what it's doing. – Ryan Stewart May 25 '12 at 03:53
  • @RyanStewart: Sorry about that. I removed all target directories. Total size should be about 70k now. – Eric May 25 '12 at 04:12
0

You are using IDENTITY generation for your identifier generation strategy, so the save() call here immediately translates to the insert. Do you see any INSERT/UPDATE/DELETE SQL executed after that? If not, it is most likely that the session is just not being flushed. flushing might happen at a number of points, read the docs on flushing if you are unfamiliar.

Steve Ebersole
  • 9,339
  • 2
  • 48
  • 46
  • I tried calling sessionFactory.getCurrentSession().flush(); explicitly and it still will not save in the database. If I search the log for LAST_ACCESSED, there are 46 lines returned. However, looking at the last few lines of SQL, I see a "create table", a "select", an "insert" (but it only shows (?, ?), so I'm not sure what it's inserting - I assume this is the first save), and a "select". I'm not sure why the "create table" seems to be coming so late in the game... – Eric May 23 '12 at 16:43
  • If you want to see what Hibernate binds to those PreparedStatement parameters, enable the logging category org.hibernate.type (Hibernate 3) or org.hibernate.type.descriptor.sql.BasicBinder (Hibernate 4) to TRACE – Steve Ebersole May 23 '12 at 18:45
  • But here, I would expect an INSERT followed by an UPDATE. Like I mentioned above, you are using IDENTITY so Hibernate has to perform the INSERT as soon as you call save() – Steve Ebersole May 23 '12 at 18:46
  • Thanks for the logging tip. I will enable that when I get back home to work on it. Sounds like the SQL in the logs is in line with what seems to be happening in the app (saves the first date, but not the second). Any other thoughts on why flushing the session won't write to the DB? – Eric May 23 '12 at 19:48
  • I would not say that it "saves the first date, but not the second". It saves both dates on INSERT. Just that at the time of INSERT "last accessed" value is null, so thats what gets written. Yes, the issue you need to figure out is why the entity changes after that (to set the "last accessed") are not processed and does not translate to an UPDATE. You might be better off switching identifier generators. IDENTITY generation has other limiting problems in conjunction with ORM and I generally recommend against using it. – Steve Ebersole May 23 '12 at 20:00
  • My underlying DB is currently MySQL. I was under the assumption that IDENTITY is required for MySQL auto generated primary keys. Is that not true? If not, what do you suggest instead? – Eric May 23 '12 at 20:59