Tuesday, September 8, 2009

Aion Beta: Closed vs. Open

Q. What is the difference between all the previous Aion closed betas and the current Aion open beta?

A. The “open” beta also includes some lucky FilePlanet paid subscribers that got a limited “open” beta key.

Monday, July 27, 2009

A Tale of Two TVs...+2

I’ve owned a cheap, 20", super ultra generic Best Buy TV for the last 5 years. It has been wonderfully reliable and well used. I think my desire for something bigger first started when I played Gears of War at a friend’s house on a large screen. The display and experience was truly awesome. Since then I’ve also felt my little screen didn’t do justice to The Legend of Zelda: Twilight Princess. Old news to anyone in the know, but I was crushed to learn the Wii doesn’t output anything HD. It does do wide screen at lest, so a new TV wouldn’t be completely wasted on it (and good luck finding a 480p wide screen).

It wasn’t until I had researched 2 months, purchased 4 TVs, then returned 3 of them, that all my requirements had been flushed out.

  • 32" wide-screen LCD
  • Closed Captioning on mute
  • The closed captioning starts appearing shortly after pressing mute
  • Passes The Legend of Zelda: Twilight Princess display calibration screen
  • Minimal input-to-display lag
  • Easily returnable

The 4th TV finally met my needs (almost). Given how hard it is to find any worthwhile information on TVs (and all electronics in general) on the internet, thought I’d share some details.

Consumer Reports

I’ve been an off-and-on registered member of Consumer Reports. Unlike fickle forums, their information is objective and substantiated. Unfortunately their TVs coverage is pretty lacking (but given the number of TVs on the market, it’s unlikely they could really keep up). If your TV of interest isn’t reviewed on the site, then the only valuable information they offer is a general brand reliability rating. Their surveys give a glimpse of what percentage of TVs are defective for a particular brand. The numbers seem to support the idea that brands do maintain a consistent level of reliability.

Vizio 32" VO320E — $400 (normally $450)

As a newcomer to the big screen TV market, I was surprised that the otherwise unheard of Vizio brand is a major player. Consumer Reports rate their reliability as one of the best. I hear they also have a pretty hefty market share. This TV on sale at Walmart has an excellent cost-to-quality ratio.

Since it was my first TV, it wasn’t until after I purchased it that I realized I really like how my old CRT could show Closed Captioning when muted. This isn’t a feature most people even realize exists, and it is never mentioned on retail sites. You have to go to the manufacturer’s site, download the product’s manual and read all the details. In any case, I strongly recommend people do that for any TV they plan to buy.

The only other down side of this TV is the simplistic remote. There are no shortcuts to menu options on the remote. For example, if you want to change the “zoom” display of a channel, it takes 5-6 button presses to traverse the menu system and change the settings.

Insignia (Best Buy generic) 32" NS-L32Q-10A — $400

After additional research, I narrowed it down to this Insignia and the RCA below. When I learned from an employee that Insignia (a generic Best Buy brand) is at least partially made by LG, I went for this one. Consumer Reports says LG reliability isn’t one of the best, but at least it’s on the list (unlike RCA).

The picture quality of this TV looks quite nice, especially when compared next to the RCA, and it has my coveted Closed Captioning on mute.

Sadly it was lacking in several other ways. The signal to some of my cable channels is a bit weak, so the picture is a little fuzzy on my CRT. On the Insignia, this weak signal sometimes exhibits itself as no audio. I found the sound could be acquired by turning the TV off, then back on. Unfortunately the sound goes away again if you change to another channel and return. This didn’t always happen, but it was enough to be very troubling. In general, this TV doesn’t handle corrupted video signal very well.

Overall the TV is just very slow. It takes about 10 to 15 seconds to turn on, and channel changing often takes like 3 to 4 seconds.

While all this is pretty lame, it was still usable—that is until I tried to play video games on it. It has a seriously bad lag from receiving input to displaying the image.

I don’t think there’s any official name for this “lag” value, and it’s never reported by TV manufacturers. Not all hope is lost though. There does seem to be some brand consistency. Looking around Best Buy, I noticed most, if not all the playable video game demos were setup on Samsung LCDs. I suspect Sonys are generally pretty good with lag as well. You can also find a sort of “game mode” feature on various TVs that skips all the image processing and just blasts the video to the screen as quickly as possible.

RCA 32" L32HD41 — $350 (normally $400)

According to the specs, this TV has a higher contrast ratio than the Insignia, but it sure didn’t look like that when sitting side-by-side. The picture was very dark and bland, and never could get bright enough, or with enough contrast to pass the Zelda Twilight Princess calibration screen.

Everything else about the TV is fine. Its input-to-display lag was pretty good, and it’s the quickest to display CC after the mute button was pressed.

Sony - BRAVIA 32" KDL-32L5000 — $500 (normally $550)

After three <= $400 TVs, it was pretty clear I can’t be cheap if I want a TV to really meet my needs. It was either this Sony or a Toshiba of the same price. I found a mention of Playstation 3 in this TV’s manual (on the Sony site). I hoped this meant its lag was small enough for video games. Sure enough, its input-to-display lag is very usable.

The picture passes Zelda Twilight Princess screen calibration without problems. All-in-all it’s quite a nice TV. The only thing about it that bugs me is the 30 seconds it takes for Closed Captioning to start showing after mute is pressed. That delay is also reset if you press pretty much any button on the remote. Makes me wish there was more interest in TV firmware hacking out there.

Final notes

I have yet to find a Samsung that does CC on mute.

I’ve now dropped about $100 in new cables (all ordered online to avoid highway robbery) to make my home theater system complete. So this whole purchase has set met back over $600. I will also be paying higher energy costs for this new TV vs. my old CRT. That little guy will be missed.

Friday, June 26, 2009

Compounding Hibernate Problems 2: The Enterprise Solution Strikes Back

It is a dark time for the
developers. Although Hibernate
can handle simple schema designs,
existing databases have driven the
coders from their lighthearted
news aggregation sites to
pursue answers from across
the internet.

Again I’m trying to add some table relations with Hibernate’s Enterprise handling of composite primary keys getting in the way.

Back with good ol' Derby.

jdbc:derby://localhost:1527/MyDB;create=true

username=myschema
password=whatever
ddl
create table vehicle (
    make_id integer,
    model_id integer,
    name varchar(50),
    PRIMARY KEY (make_id, model_id)
);

create table maker_country (
    make_id integer,
    country_id integer,
    make_country varchar(50),
    PRIMARY KEY (make_id, country_id)
);


insert into vehicle values (1, 0, 'Chevrolet Corvette');
insert into vehicle values (1, 1, 'Chevrolet Malibu');
insert into vehicle values (2, 0, 'Ford Focus');
insert into vehicle values (2, 1, 'Ford Mustang');
insert into vehicle values (3, 0, 'Honda Accord');
insert into vehicle values (3, 1, 'Honda Odyssey');
insert into vehicle values (4, 0, 'Toyota Avalon');
insert into vehicle values (4, 1, 'Toyota Camry');
insert into vehicle values (5, 1, 'Volkswagen Bug');

insert into maker_country values (1, 1, 'Chevrolet makes cars for USA');
insert into maker_country values (2, 1, 'Ford makes cars for USA');
insert into maker_country values (3, 2, 'Honda makes cars for Japan');
insert into maker_country values (3, 1, 'Honda makes cars for USA');
insert into maker_country values (4, 2, 'Toyota makes cars for Japan');
insert into maker_country values (4, 1, 'Toyota makes cars for USA');
insert into maker_country values (5, 3, 'Volkswagen makes cars for Germany');
insert into maker_country values (5, 1, 'Volkswagen makes cars for USA');
.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">org.apache.derby.jdbc.EmbeddedDriver</property>
        <property name="hibernate.dialect">org.hibernate.dialect.DerbyDialect</property>
        <property name="hibernate.connection.url">jdbc:derby://localhost:1527/MyDB;create=true</property>
        <property name="hibernate.connection.username">myschema</property>
        <property name="hibernate.connection.password">whatever</property>
        <property name="hibernate.default_schema">MYSCHEMA</property>
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.show_sql">true</property>
    </session-factory>
</hibernate-configuration>

Of course we know this won’t work…

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-reverse-engineering PUBLIC
    "-//Hibernate/Hibernate Reverse Engineering DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-reverse-engineering-3.0.dtd" >

<hibernate-reverse-engineering>
    <table-filter match-name="MAKER_COUNTRY"/>
    <table-filter match-name="VEHICLE"/>

    <table name="VEHICLE">
        <foreign-key foreign-table="MAKER_COUNTRY">
            <column-ref local-column="MAKE_ID" foreign-column="MAKE_ID" />
        </foreign-key>
    </table>

</hibernate-reverse-engineering>
Foreign key (FK3F2F1B0C64FEF3DE:VEHICLE [MAKE_ID])) must have same number of columns as the referenced primary key (MAKER_COUNTRY [MAKE_ID,COUNTRY_ID])

So we stick with the default generated reverse engineering. We can use it to generate only the mapping files (no .java classes yet).

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-reverse-engineering PUBLIC
    "-//Hibernate/Hibernate Reverse Engineering DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-reverse-engineering-3.0.dtd" >

<hibernate-reverse-engineering>
    <table-filter match-name="MAKER_COUNTRY"/>
    <table-filter match-name="VEHICLE"/>
</hibernate-reverse-engineering>

Then we can edit the mapping file to add a relationship between two tables.

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="Vehicle" table="VEHICLE">
        <composite-id name="id" class="VehicleId">
            <key-property name="makeId" type="int">
                <column name="MAKE_ID" />
            </key-property>
            <key-property name="modelId" type="int">
                <column name="MODEL_ID" />
            </key-property>
        </composite-id>
        <property name="name" type="string">
            <column name="NAME" length="50" />
        </property>

        <!-- Added this: -->
        <set name="makerCountries" inverse="false" lazy="true">
            <key property-ref="makeId" column="MAKE_ID" />
            <one-to-many class="MakerCountry" />
        </set>

    </class>
</hibernate-mapping>

No problem, right?

org.hibernate.MappingException: property-ref [makeId] not found on entity [Vehicle]

Ok, so it can’t see its own property. What if reference its key’s property.

        ...
            <key property-ref="id.makeId" column="MAKE_ID" />
        ...

It generated some classes. Let’s give it a run.

Test.java
import java.net.URL;
import java.util.List;
import java.util.Set;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

public class Test {
    static SessionFactory sessionFactory;
    static Session session;
    public static void main(String[] args) {
        URL url = Test.class.getResource("/hibernate.cfg.xml");
        sessionFactory = new AnnotationConfiguration().configure(url).buildSessionFactory();
        session = sessionFactory.getCurrentSession();
        org.hibernate.Transaction tx = session.beginTransaction();

        Query q = session.createQuery("from Vehicle as v where v.id.makeId = 1");
        List<Vehicle> lst = null;
        lst = q.list();
        if (lst.size() > 0) {
            Vehicle v = lst.get(0);
            System.out.println(v.getName());
            Set<MakerCountry> makerCountries = v.getMakerCountries();
            System.out.println(makerCountries.size());
        }
    }
}

Nope.

Hibernate: select vehicle0_.MAKE_ID as MAKE1_1_, vehicle0_.MODEL_ID as MODEL2_1_, vehicle0_.NAME as NAME1_ from MYSCHEMA.VEHICLE vehicle0_ where vehicle0_.MAKE_ID=1
Exception in thread "main" org.hibernate.HibernateException: Unable to resolve property: id.makeId

Fine, we’ll add the property ourselves.

        ...
        <property name="makeId" type="int" insert="false" update="false">
            <column name="MAKE_ID" />
        </property>
        <set name="makerCountries" inverse="false" lazy="true">
            <key property-ref="makeId" column="MAKE_ID" />
            <one-to-many class="MakerCountry" />
        </set>
        ...

Now umm…

Hibernate: select vehicle0_.MAKE_ID as MAKE1_1_, vehicle0_.MODEL_ID as MODEL2_1_, vehicle0_.NAME as NAME1_ from MYSCHEMA.VEHICLE vehicle0_ where vehicle0_.MAKE_ID=1
Chevrolet Corvette
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: Vehicle.makerCountries, no session or session was closed

"no session or session was closed"?? What does that mean?

Whatever, we’ll set lazy to false.

        ...
        <set name="makerCountries" inverse="false" lazy="false">
        ...

and…

Hibernate: select vehicle0_.MAKE_ID as MAKE1_1_, vehicle0_.MODEL_ID as MODEL2_1_, vehicle0_.NAME as NAME1_ from MYSCHEMA.VEHICLE vehicle0_ where vehicle0_.MAKE_ID=1
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer

I think Luke Skywalker summed it up quite poignantly when he said

NOOOOOOOOOOOOOO!!

Tuesday, June 23, 2009

Lazy Hibernate

Let’s play with large collections.

ddl
CREATE TABLE popular_band (
    band_id AS INTEGER PRIMARY KEY,
    band_name AS VARCHAR
);

CREATE TABLE fan (
    fan_id AS INTEGER PRIMARY KEY,
    fan_name AS VARCHAR,
    favorite_band_id AS INTEGER
);

ALTER TABLE fan ADD fans_fk_popular_band FOREIGN KEY (favorite_band_id)
REFERENCES popular_band(band_id);

INSERT INTO popular_band VALUES (1, "Beatles");

INSERT INTO fan VALUES (1, "Jim", 1);
INSERT INTO fan VALUES (2, "Bob", 1);
INSERT INTO fan VALUES (3, "Sue", 1);
INSERT INTO fan VALUES (4, "Mary", 1);
-- 100,000 more ...
INSERT INTO fan VALUES (100005, "Alex", 1);

After reverse engineering this schema with Hibernate, your mappings should look something like this.

PopularBand.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="PopularBand" table="POPULAR_BAND">
        <id name="bandId" type="long">
            <column name="BAND_ID" />
        </id>
        <property name="bandName" type="string">
            <column name="BAND_NAME" />
        </property>
        <set name="fans" inverse="true" lazy="true" table="FAN" fetch="select">
            <key>
                <column name="FAVORITE_BAND_ID" precision="10" scale="0" />
            </key>
            <one-to-many class="Fan" />
        </set>
    </class>

    <class name="Fan" table="FAN">
        <id name="fanId" type="long">
            <column name="FAN_ID" />
        </id>
        <property name="fanName" type="string">
            <column name="FAN_NAME" />
        </property>
        <many-to-one name="favoriteBand" class="PopularBand" fetch="select">
            <column name="FAVORITE_BAND_ID" />
        </many-to-one>
    </class>
</hibernate-mapping>

The Beatles, being a very popular band, have many fans. I just want to look at 10, in no particular order. That shouldn’t be hard, right?

public static void print10fans(PopularBand pb) {
    Set<Fan> fans = pb.getFans();
    Iterator<Fan> iter = fans.iterator();
    for (int i=0; i < 10 && iter.hasNext(); i++) {
        System.out.println(iter.next().getFanName());
    }
}

And the output is

java.lang.OutOfMemoryError: Java heap space
...

Oops, our very large collection is very large. Maybe the manual can help us.

lazy (optional - defaults to true) may be used to disable lazy fetching and specify that the association is always eagerly fetched, or to enable "extra-lazy" fetching where most operations do not initialize the collection (suitable for very large collections)

Perfect! Just a quick change

<set name="fans" inverse="true" lazy="extra" table="FAN" fetch="select">
                                      ^^^^^

and we can try again.

java.lang.OutOfMemoryError: Java heap space
...

Ok, just what does this "extra-lazy" really mean anyway? The manual is very silent on such details, so we turn to other sources (emphasis added).

Extra lazy doesn’t do much on sets. It is actually quite useful only on the size method which triggers a select count on the database. However, iterating over a set will instantiate the whole collection. On maps and lists, extra lazy also enables fetching an entry by index. Still iterating over a list or a map will fetch the whole collection from database.

So when the manual says

…most operations do not initialize the collection (suitable for very large collections)

it actually means

…only the size() method does not initialize the collection, unless it’s a map or list, then you can also fetch by index (but otherwise your very large collection is destined to crash your program)

You can use "Filtering collections" to specify how many results you want, but doing that seems to turn the whole idea of “Plain-Old-Java-Objects” into “Plain-Old-Java-Objects-Unless-Your-Collections-Are-Too-Big-Then-You-Have-To-Use-Framework-Specific-API”

Monday, May 11, 2009

Fastest bulk import into sqlite

I just wanted to populate a sqlite database with 100 million rows of data from a 1.8 gigabyte tab delimited file (tsv).

commands.txt
.echo ON

.read create_table_without_pk.sql

PRAGMA cache_size = 400000;
PRAGMA synchronous = OFF;
PRAGMA journal_mode = OFF;
PRAGMA locking_mode = EXCLUSIVE;
PRAGMA count_changes = OFF;
PRAGMA temp_store = MEMORY;
PRAGMA auto_vacuum = NONE;

.separator "\t"
.import a_tab_seprated_table.txt mytable

BEGIN;
.read add_indexes.sql
COMMIT;

.exit

PRAGMA cache_size = 400000 lets sqlite allocate about 460MB of RAM to work with. You can find more detailed explaination of these PRAGMA options on the Sqlite site.

sqlite3 mydb.db < commands.txt

Using this approach, the inserting of data took only about 20 mins.

However, adding a composite primary key (i.e. unique index) and a non-unique composite index ran for 70+ hours before I just canceled it.

If you can pre-sort your data in the order it is indexed, the import speed is practically the same whether you create the index before or after the import. However, if you create the index before the import, the database data and indexes will be intermingled with each other, thus fragmented (but this won’t affect the file size). The fragmentation can be corrected by running the VACUUM command.

There is a powerful ExternalSort implementation as part of the Java Small Text library. Look under DocumentationSmall Text Utils tutorial for info about it.

Friday, May 1, 2009

Compounding Hibernate Problems

Platform information
Eclipse Platform
Version: 3.4.2
Build id: M20090211-1700

Hibernate Tools 3.2.4.GA-R200903141626-H5
and
Hibernate Tools 3.2.4.GA-N200904280907-H52

Windows XP Pro SP3

JRE 6

Tested on Oracle 10g and Apache Derby

Two problems with Hibernate Tools reverse engineering:

  1. Unable to create a relationship from a non-primary key column in one table to one column of a composite primary key in another table, and visa-versa.

  2. Creating a relationship from a column in a composite primary key of one table to a non-primary key column in another table produces incorrect mapping file (I have submitted a poorly written bug report for this one).

These issues will be explorered in the following example. For simplicity we’ll use an Apache Derby database.

Start the database, and connect to it.

jdbc:derby://localhost:1527/myDB;create=true;user=me;password=mine

Create these tables in the myDB database, in the ME schema.

ddl
create table vehicles (
    make_id integer,
    model_id integer,
    name varchar(50),
    PRIMARY KEY (make_id, model_id)
);

create table owners (
    owner_id integer PRIMARY KEY,
    favorite_make_id integer,
    name varchar(20)
);

insert into vehicles values (1, 0, 'Chevrolet Corvette');
insert into vehicles values (1, 1, 'Chevrolet Malibu');
insert into vehicles values (2, 0, 'Ford Focus');
insert into vehicles values (2, 1, 'Ford Mustang');
insert into vehicles values (3, 0, 'Honda Accord');
insert into vehicles values (3, 1, 'Honda Odyssey');
insert into vehicles values (4, 0, 'Toyota Avalon');
insert into vehicles values (4, 1, 'Toyota Camry');

insert into owners values (50, 3, 'Jim');
insert into owners values (51, 1, 'Alex');
insert into owners values (52, 4, 'Sue');
insert into owners values (53, 1, 'Casey');
insert into owners values (54, 3, 'Ted');
insert into owners values (55, 2, 'Mary');

Create this Hibernate Configuration file.

.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">org.apache.derby.jdbc.EmbeddedDriver</property>
        <property name="hibernate.connection.url">jdbc:derby://localhost:1527/myDB</property>
        <property name="hibernate.connection.username">me</property>
        <property name="hibernate.connection.password">mine</property>
        <property name="hibernate.dialect">org.hibernate.dialect.DerbyDialect</property>
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.default_schema">ME</property>
        <property name="hibernate.show_sql">true</property>
    </session-factory>
</hibernate-configuration>

Setup a Hibernate Console Configuration with the configuration above and connection to the Derby database.

Create this Hibernate reverse engineering configuration.

.reveng.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-reverse-engineering PUBLIC
    "-//Hibernate/Hibernate Reverse Engineering DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-reverse-engineering-3.0.dtd" >

<hibernate-reverse-engineering>
    <table-filter match-name="OWNERS"/>
    <table-filter match-name="VEHICLES"/>

    <table name="OWNERS">
        <foreign-key foreign-table="VEHICLES">
            <column-ref local-column="FAVORITE_MAKE_ID" foreign-column="MAKE_ID" />
        </foreign-key>
    </table>

</hibernate-reverse-engineering>

Setup a Hibernate Code Generation configuration like this:
[Main tab] [Exporters tab]

Trying to run it produces this error:

org.hibernate.MappingException: Foreign key (FK8BBD58205C954C50:OWNERS [FAVORITE_MAKE_ID])) must have same number of columns as the referenced primary key (VEHICLES [MAKE_ID,MODEL_ID])
Foreign key (FK8BBD58205C954C50:OWNERS [FAVORITE_MAKE_ID])) must have same number of columns as the referenced primary key (VEHICLES [MAKE_ID,MODEL_ID])

Searching for "must have same number of columns as the referenced primary key" on Google returns 6 pages of results. You can find references to this error as early as 2005.

The Other Way (incorrect)

So let’s try the other way, even though it’s incorrect.

Change the Hibernate reverse engineering configuration to this.

.reveng.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-reverse-engineering PUBLIC
    "-//Hibernate/Hibernate Reverse Engineering DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-reverse-engineering-3.0.dtd" >

<hibernate-reverse-engineering>
    <table-filter match-name="OWNERS"/>
    <table-filter match-name="VEHICLES"/>

    <table name="VEHICLES">
        <foreign-key foreign-table="OWNERS">
            <column-ref local-column="MAKE_ID" foreign-column="FAVORITE_MAKE_ID" />
        </foreign-key>
    </table>

</hibernate-reverse-engineering>

Now the Code Generation configuration will run, but it produces an incorrect mapping file.

Vehicles.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="Vehicles" table="VEHICLES">
        <composite-id name="id" class="VehiclesId">
            <key-property name="makeId" type="int">
                <column name="MAKE_ID" />
            </key-property>
            <key-property name="modelId" type="int">
                <column name="MODEL_ID" />
            </key-property>
        </composite-id>
        <many-to-one name="owners" class="Owners" update="false" insert="false" fetch="select">
            <column name="MAKE_ID" not-null="true" />
        </many-to-one>
        <property name="name" type="string">
            <column name="NAME" length="50" />
        </property>
    </class>
</hibernate-mapping>

The many-to-one tag should have this additional attribute:

property-ref="favoriteMakeId"

Because it doesn’t, this code will fail:

Main.java
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.*;

public class Main {

    public static void main(String[] args) {
        System.out.println("Connecting to Derby database.");
        SessionFactory sf = new AnnotationConfiguration().configure().buildSessionFactory();
        Session session = sf.getCurrentSession();

        System.out.println("Querying for some vehciles.");
        session.beginTransaction();
        Query q = session.createQuery("from Vehicles as v where v.id.modelId = 0");
        java.util.List<Vehicles> vehicleList = q.list();

        System.out.println("Got " + vehicleList.size() + " matches.");

        for (Vehicles vehicle : vehicleList) {

            System.out.println("Getting an owner that likes the maker of " + vehicle.getName());
            Owners owner = vehicle.getOwners();
            try {
                System.out.println(owner.getName() + " likes it.");
            } catch (ObjectNotFoundException ex) {
                ex.printStackTrace();
                System.err.println("This fails because it was matching VEHCILES.MAKE_ID to OWNERS.OWNER_ID");
                System.err.println("when it SHOULD have matched to OWNERS.FAVORITE_MAKE_ID like the reveng says.");
            }
        }
    }

}

Program output:

Connecting to Derby database.
Querying for some vehciles.
Hibernate: select vehicles0_.MAKE_ID as MAKE1_0_, vehicles0_.MODEL_ID as MODEL2_0_, vehicles0_.NAME as NAME0_ from ME.VEHICLES vehicles0_ where vehicles0_.MODEL_ID=0
Got 4 matches.
Getting an owner that likes the maker of Chevrolet Corvette
Hibernate: select owners0_.OWNER_ID as OWNER1_1_0_, owners0_.FAVORITE_MAKE_ID as FAVORITE2_1_0_, owners0_.NAME as NAME1_0_ from ME.OWNERS owners0_ where owners0_.OWNER_ID=?
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [Owners#1]
    at org.hibernate.impl.SessionFactoryImpl$2.handleEntityNotFound(SessionFactoryImpl.java:409)
    at org.hibernate.proxy.AbstractLazyInitializer.checkTargetState(AbstractLazyInitializer.java:108)
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:97)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:140)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at Owners_$$_javassist_1.getName(Owners_$$_javassist_1.java)
    at Main.main(Main.java:23)
This fails because it was matching VEHCILES.MAKE_ID to OWNERS.OWNER_ID
...

However, even if you manually add the property-ref, when you run it with the example program, it returns an error of “More than one row with the given identifier was found”.

Connecting to Derby database.
Querying for some vehciles.
Hibernate: select vehicles0_.MAKE_ID as MAKE1_0_, vehicles0_.MODEL_ID as MODEL2_0_, vehicles0_.NAME as NAME0_ from ME.VEHICLES vehicles0_ where vehicles0_.MODEL_ID=0
Hibernate: select owners0_.OWNER_ID as OWNER1_1_0_, owners0_.FAVORITE_MAKE_ID as FAVORITE2_1_0_, owners0_.NAME as NAME1_0_ from ME.OWNERS owners0_ where owners0_.FAVORITE_MAKE_ID=?
Exception in thread "main" org.hibernate.HibernateException: More than one row with the given identifier was found: 1, for class: Owners
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:92)
    at org.hibernate.loader.entity.EntityLoader.loadByUniqueKey(EntityLoader.java:108)
    at org.hibernate.persister.entity.AbstractEntityPersister.loadByUniqueKey(AbstractEntityPersister.java:1672)
    at org.hibernate.type.EntityType.loadByUniqueKey(EntityType.java:641)
    at org.hibernate.type.EntityType.resolve(EntityType.java:415)
    at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:139)
    at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:877)
    at org.hibernate.loader.Loader.doQuery(Loader.java:752)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
    at org.hibernate.loader.Loader.doList(Loader.java:2228)
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2125)
    at org.hibernate.loader.Loader.list(Loader.java:2120)
    at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:401)
    at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:361)
    at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:196)
    at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1148)
    at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)
    at Main.main(Main.java:14)

This is because the example column has non-unique entries. If it only had unique entires, our manual property-ref fix would work.

Doing it manually

Hibernate reverse engineering has pretty much no support for special table relationships (e.g. a non-inverse one-to-many). The only real solution to this problem is to manually create the mapping files and classes yourself.

Some links that probably won’t help at all:

Trying to use a composite-id with key-many-to-one won’t work because the foreign column needs to be a primary key.

Here is how the mapping of Vehicles → Owners should be:

Vehicles.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="Vehicles" table="VEHICLES">
        <composite-id mapped="false" >
            <key-property name="makeId" type="int" >
                <column name="MAKE_ID" />
            </key-property>
            <key-property name="modelId" type="int">
                <column name="MODEL_ID" />
            </key-property>
        </composite-id>
        <property name="name" type="string">
            <column name="NAME" length="50" />
        </property>
        <bag name="owners">
            <key>
                <column name="FAVORITE_MAKE_ID" />
            </key>
            <one-to-many class="Owners" />
        </bag>
    </class>
</hibernate-mapping>

Some problems here:

  • Any kind of collection mapping (set, list, bag, array, map) require a key element be defined.
  • The key generates an error because it doesn’t map the “same number of columns” (i.e. it needs 2 columns to match the 2 column primary key). It’s saying it needs another key defined.
  • So why not just get rid of the primary key? Because the class element requires a id or composite-id element. And we can’t make just MAKE_ID or MODEL_ID the primary key because those won’t be unique.

Solution

At long last! For Owners → Vehicles, this actually works.

Owners.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="Owners" table="OWNERS">
        <id name="ownerId" type="int">
            <column name="OWNER_ID" />
            <generator class="assigned" />
        </id>
        <property name="favoriteMakeId" type="int">
            <column name="FAVORITE_MAKE_ID" />
        </property>
        <property name="name" type="string">
            <column name="NAME" length="20" />
        </property>
        <set name="vehicles" inverse="false">
            <key property-ref="favoriteMakeId" column="MAKE_ID" />
            <one-to-many class="Vehicles" />
        </set>
      </class>
</hibernate-mapping>

There still doesn’t appear to be any method to properly associate Vehicles → Owners, but thankfully the mapping file above is all I really need for my case.

There also doesn’t appear to be any way to produce a mapping file like this using Hibernate Tools reverse engineering, so I just have to make it manually.

Going back through some of the many links I visited while searching for help, I noticed this page also basically explains the solution above.