tags:

views:

508

answers:

4

I have an entity class:

Cabbage.java

package womble;

public class Cabbage {
    private int id;
    // getters, setters, etc.
}

That I've got mapped to two separate database tables as follows:

Cabbage.hbm.xml

<hibernate-mapping>
  <class name="womble.Cabbage"> <!-- Using the default entity and table name. -->
    <id name="id" unsaved-value="null">
      <generator class="native"/>
    </id>
  </class>
</hibernate-mapping>

CabbageBackup.hbm.xml

<hibernate-mapping>
  <class name="womble.Cabbage" entity-name="CabbageBackup" table="cabbage_backup">
    <id name="id">
      <generator class="assigned"/>
    </id>
  </class>
</hibernate-mapping>

I'm trying to retrieve the last saved Cabbage instance like this:

CabbageDao.java

package womble;

public class CabbageDao {
    Session session; // Hibernate session
    String GET_MAX_ID = "select max(id) from Cabbage";

    public getLastCabbage() {
        Object lastId = session.createQuery(GET_MAX_ID).uniqueResult();
        return session.load(Cabbage.class, lastId);
    }
}

This blows up with a NonUniqueResultException: "query did not return a unique result: 2"

Looking through the logs, Hibernate apparently queries both the tables and then joins the results together.

Is there a way to make Hibernate only query the default mapping for an entity? (That is, to specifically prevent it from magically querying both mappings when I use the simple class name.)

I tried using the above, using the criteria API passing Cabbage.class to #createCriteria(), as well as the string "Cabbage", both select from both tables. I'm trying to avoid specifying an entity name to the "main" mapping, because there's multiple classes like that in my code and I'd have to fix up all the relationships between them as well.

A: 

Try using an entity-name on both mappings, and specify which you'd like. I believe that is the contract of that attribute.

eqbridges
I'm keeping that as a last resort, because then I'd need to edit a good deal of my non-backup mapping files and hope noone comes running after me with an axe when not using it won't work. (I surprised my boss with "Well, you can map a class to multiple tables.")
Sii
Accepted the answer because it's what I ended up doing.
Sii
A: 

Well, an easy answer (not necessarily the best one) would be to use .list() instead of .uniqueResult() bringing back a Collection of the max ids. You could then toss the results into a new HashSet and let Java do the work of unique-ing them. Not elegant, but it would work.

public getLastCabbage() {
    Set lastIds = new HashSet(session.createQuery(GET_MAX_ID).list());
    // ignore the possibility of null; full steam ahead
    return session.load(Cabbage.class, lastIds.iterator().next());
}
dustmachine
I need specifically results from the "cabbage" table and not "cabbage_backup"
Sii
So recent cabbages can be removed from the cabbage table and then cabbage_backup would have a larger id? Is there any other indicator that could be used in the query to identify what makes an entry a current cabbage or a backup-cabbage?
dustmachine
This was just an example query, I really have multiple cases like this when I never want to query backups during regular operation. They're really only there just in case.
Sii
+2  A: 

Firstly, Hibernate has no notion here of your "default" - both mappings are on an equal footing as far as it's concerned.

Do you ever need to access old cabbages (not a phrase I've ever used before) from the backup table, or are you just using that table for archiving old data?

If the latter, then there are probably better ways of achieving the same result, such as using a database trigger.

skaffman
It's for archiving old data. This is a point of sale fat -ish client app, so it's really quite important that deployment be as few steps past "copy onto hard drive" as possible, and having Hibernate do schema creation / updates really helps.I might not need to access the old data too often (probably never), but the requirement is there and being able to do this in a single Hibernate session would make my life much easier.
Sii
So it sounds like a database trigger is what you need - when the database detects a Cabbage being inserted or updated, it takes a copy of the existing data and sticks into the archive table. Hibernate need not know about it. What DBMS are you using?
skaffman
Or, if you really need access to both tables through hibernate, extending the cabbage class to cabbage_backup and using that class as the hbm for the backup class. There may be some subtleties there that I am missing (I have not done much with class extension in hibernate) but that might get you on the right track. However, this is correct - hibernate has no way of distinguishing between the different tables - the class is mapped to both, so it uses both tables.
aperkins
A: 

You might consider one of the extensions to hibernate which do this table revisions for you. I've never used envers personally, but the documentation looked interesting.

Petriborg