5

I would like to have something like this be generated from hbm2ddl:

______________    ______________       _______________
|Language    |    |I18N        |       |Test         |
--------------    --------------       ---------------
|iso3_code:PK|----|iso3_code:PK|       |test_id:PK   |
--------------    |i18n_id:PK  |-------|desc_i18n_id |
                  |i18n_text   |     |-|labl_i18n_id |
                  --------------       ---------------

This means more or less that, there is a table language, which holds the iso code and maybe some other info. The i18n table has a foreign key iso3_code on the language table which is also a primary key. The other part of the PK is the i18n_id. The test table then has two foreign keys on the table i18n on the field i18n_id.

The output of the parsed hbm2ddl should be like this:

public class Test  implements java.io.Serializable {
    private Integer testId;
    private Map<String,String> label = new HashMap<String,String>(0);
    private Map<String,String> description = new HashMap<String,String>(0);

    public Test() {
    }

    public Integer getTestId() {
        return this.testId;
    }

    public void setTestId(Integer testId) {
        this.testId = testId;
    }

    public Map<String, String> getLabel() {
        return label;
    }

    public void setLabel(Map<String,String> label) {
        this.label = label;
    }

    public Map<String, String> getDescription () {
        return description ;
    }

    public void setDescription (Map<String,String> description ) {
        this.description = description ;
    }

}

So now the question is, how has my hbm.xml file to look like to generate this table structure and this class. Even if i can not create all resources fully automatically, I would really like to know how this should be declared. I already got it to work for selects, but not for inserts or updates.

<class name="test.Test" table="test" catalog="testdb">
    <id name="testId" type="java.lang.Integer">
        <column name="test_id" />
        <generator class="native" />
    </id>
    <map name="label" table="i18n" fetch="join" cascade="all">
        <key column="i18n_id" not-null="true" foreign-key="label_id"/>
        <map-key column="iso3_code" type="string"/>
        <element column="i18n_text" type="string"/>
    </map>
</class>

<class name="test.Lang" table="lang" catalog="testdb">
    <id name="iso3Code" type="string">
        <column name="iso3_code" length="4" />
        <generator class="assigned" />
    </id>
</class>

<class name="test.I18n" table="i18n" catalog="testdb">
    <composite-id name="id" class="com.blazebit.test.I18nId">
        <key-property name="i18nId" type="int">
            <column name="i18n_id" />
        </key-property>
        <key-property name="iso3Code" type="string">
            <column name="iso3_code" length="4" />
        </key-property>
    </composite-id>
    <property name="i18nText" type="string">
        <column name="i18n_text" />
    </property>
</class>

I do not really know why the insert does not work, but maybe it is because the I18nId object which should identify a text, can not be generated. In case of this, i would also accept a solution like this: Map getLabel(){}

But with this solution another problem will arise, the i18n_id can not be set by mysql with auto_increment. It would be possible without hibernate.

Please anybody help me or give a better practice on how to implement this!

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58

1 Answers1

8

I know my question is very old, but probably someone finds this and wants to know how to do that!

Well my final solution is creating embedded or composite elements within a map. Pretty easy, but you have to know how to do that. Here is an example on how to do that with annotations:

@Entity
@Table(name="A")
public class A implements Serializable{

    private Map<Locale, LocalizedA> localized = new HashMap<Locale, LocalizedA>();

    @ElementCollection
    @CollectionTable(name = "localized_a")
    @MapKeyJoinColumn(name = "field_name_for_locale")
    public Map<Locale, LocalizedA> getLocalized() {
        return this.localized;
    }

    public void setLocalized(Map<Locale, LocalizedA> localized) {
        this.localized = localized;
    }

}


@Embeddable
public class LocalizedA implements java.io.Serializable {

    @Column(name = "field_name_for_description")
    public String getDescription() {
        return this.description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

I hope this will help someone, if you want an example for hbm.xml files just comment and i will add that.

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58
  • the hbm.xml files will be really helpful indeed though I am quite late to the party – Gautam Jun 16 '14 at 08:24
  • 1
    Sorry but I don't use hbm.xml files anymore. I completely switched to annotations. I think it shouldn't be that difficult to create hbm.xml files based on the annotation solution. – Christian Beikov Jun 19 '14 at 07:20
  • I am very late to the party, but it seems like you have a very elegant solution. However from your example im struggling to connect the dots. Would you be able to share the full solution like you showcased in the original post (3 table example)? – zalis Nov 29 '16 at 16:08
  • What is it exactly that you don't understand? Basically there are 2 tables involved in this but you could also add the third table if you used an Entity as key. Table A is everything except for the internationalizied parts and the Embeddable LocalizedA contains all the field for a localization. The localizations are put into a table named 'localized_a' which has the id reference to the Table A, the language code as 'field_name_for_locale' and all the columns defined by the Embeddable LocalizedA. – Christian Beikov Nov 29 '16 at 16:30
  • Thanks Christian, would you be able to share the "main" so i can see the usage. Your example does create a table structure that Im after, but it fails when i attempt to persist the data – zalis Nov 30 '16 at 08:02
  • What do you mean by fail? Essentially you only have to populate the map and call `em.persist()` on instances of your Entity A. – Christian Beikov Nov 30 '16 at 10:12