Implementing EAV pattern with Hibernate for User -> Settings relationship
I'm trying to setup a simple EAV pattern in my web app using Java/Spring MVC and Hibernate. I can't seem to figure out the magic behind the hibernate XML setup for this scenario.
My database table "SETUP" has three columns:
- user_id (FK)
- setup_item
- set开发者_Python百科up_value
The database composite key is made up of user_id | setup_item
Here's the Setup.java class:
public class Setup implements CommonFormElements, Serializable {
  private Map data = new HashMap();
  private String saveAction;
  private Integer speciesNamingList;
  private User user;
  Logger log = LoggerFactory.getLogger(Setup.class);
  public String getSaveAction() {
    return saveAction;
  }
  public void setSaveAction(String action) {
    this.saveAction = action;
  }
  public User getUser() {
    return user;
  }
  public void setUser(User user) {
    this.user = user;
  }
  public Integer getSpeciesNamingList() {
    return speciesNamingList;
  }
  public void setSpeciesNamingList(Integer speciesNamingList) {
    this.speciesNamingList = speciesNamingList;
  }
  public Map getData() {
    return data;
  }
  public void setData(Map data) {
    this.data = data;
  }
}My problem with the Hibernate setup, is that I can't seem to figure out how to map out the fact that a foreign key and the key of a map will construct the composite key of the table... this is due to a lack of experience using Hibernate. Here's my initial attempt at getting this to work:
<composite-id>
  <key-many-to-one foreign-key="id" name="user" column="user_id" class="Business.User">
    <meta attribute="use-in-equals">true</meta>
  </key-many-to-one>
</composite-id>
<map lazy="false" name="data" table="setup">
  <key column="user_id" property-ref="user"/>
  <composite-map-key class="Command.Setup">
    <key-property name="data" column="setup_item" type="string"/>
  </composite-map-key>
  <element column="setup_value" not-null="true" type="string"/>
</map>
Any insight into how to properly map this common scenario would be most appreciated!
As shown by yourself, you have a inconsistent mapping
You said Setup class defines a composite primary key (Notice i have created a composite primary key class (SetupId - see bellow) which must implements Serializable and equals and hashcode method)
package ar.domain;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
public class Setup implements Serializable {
    private SetupId setupId;
    private User user;
    private Map data= new HashMap();
    public SetupId getSetupId() {
        return setupId;
    }
    public void setSetupId(SetupId setupId) {
        this.setupId = setupId;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    public Map getData() {
        return data;
    }
    public void setData(Map data) {
        this.data = data;
    }
    public static class SetupId implements Serializable {
        private Integer userId;
        private String setupItem;
        public String getSetupItem() {
            return setupItem;
        }
        public void setSetupItem(String setupItem) {
            this.setupItem = setupItem;
        }
        public Integer getUserId() {
            return userId;
        }
        public void setUserId(Integer userId) {
            this.userId = userId;
        }
        @Override
        public boolean equals(Object o) {
            if (o == null)
                return false;
            if (!(o instanceof SetupId))
                return false;
            final SetupId other = (SetupId) o;
            if (!(getUserId().equals(other.getUserId())))
                return false;
            if (!(getSetupItem().equals(other.getSetupItem())))
                return false;
            return true;
        }
        @Override
        public int hashCode() {
            int hash = 7;
            hash = 11 * hash + (getUserId() != null ? getUserId().hashCode() : 0);
            hash = 11 * hash + (getSetupItem() != null ? getSetupItem().hashCode() : 0);
            return hash;
        }
    }
}
Because of your Setup class has a Map of value Type, you should define its composite foreign key when defining its relationship (see key element)
<class name="ar.domain.Setup">
    <composite-id name="setupId" class="ar.domain.Setup$SetupId">
        <key-property name="setupItem" type="string" column="SETUP_ITEM"/>
        <key-property name="userId" type="integer" column="USER_ID"/>
    </composite-id>
    <many-to-one name="user" class="ar.domain.User" column="USER_ID" insert="false" update="false"/>
    <map name="data" table="DATA_TABLE">
        <key>
            <column name="SETUP_ITEM"/>
            <column name="USER_ID"/>
        </key>
        <map-key column="USER_ID"/>
        <element column="SETUP_VALUE" not-null="true" type="string"/>
    </map>
</class>
And, at the same time, use a composite foreign key column as map-key (USER_ID, right ?) which does not make sense. Why ?
- Hibernate does not allow you update a (composite) primary key column
Besides that, Hibernate does not support automatic generation of composite primary key
Suppose here goes your SETUP Table
SETUP_ITEM USER_ID
0          1
0          2
And your DATA_TABLE
SETUP_ITEM USER_ID
0          1
What happens whether you try the following one
Integer userId = 3;
String setupValue = "someValue";
setup.getData().put(userId, setupValue);
Because of SETUP Table does not define a USER_ID which value is 3, you will see a constraint violation.
Keep it in mind
When you have a (composite) primary key, which can not be updatable, avoid to use it, someway, to change a mutable property which depends on it. Otherwise, Hibernate will complain it.
 
         加载中,请稍侯......
 加载中,请稍侯......
      
精彩评论