0

我正在尝试使用带有 Ebean 的 Play 2.0 重新创建我们的一个 Web 应用程序,但遇到了障碍。我不知道如何将 MySQL 的SET类型映射到模型中的字段。我已经ENUM使用注释使列工作,@Enumerated(EnumType.STRING)但我似乎找不到有关SET列的任何信息。

该表模仿 crontab:

CREATE TABLE IF NOT EXISTS `schedule` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `task_id` mediumint(8) unsigned NOT NULL default '0',
  `month` set('January','February','March','April','May','June','July','August','September','October','November','December') default NULL,
  `mday` set('1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31','-1','-2','-3','-4','-5','-6','-7','-8','-9','-10') default NULL,
  `wday` set('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday') default NULL,
  `hour` set('0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23') default NULL,
  `minute` set('00','01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35','36','37','38','39','40','41','42','43','44','45','46','47','48','49','50','51','52','53','54','55','56','57','58','59') default NULL,
  `updated` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`),
  KEY `event` (`task_id`)
)

我现在按照 MvG 的建议创建了一个 UserType 和相关注释:

@Entity
public class Schedule extends Model {

    public enum Month { JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER };
    public enum Weekday { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY };

    @Id
    public Long id;

    @ManyToOne(cascade = CascadeType.MERGE)
    public Task task;
    @Version
    public Timestamp updated;

    @Type(type="models.EnumSetUserType",parameters=@Parameter(name="enumType",value="models.Schedule$Month"))
    @Column(name="month", columnDefinition="SET('JANUARY','FEBRUARY','MARCH','APRIL','MAY','JUNE','JULY','AUGUST','SEPTEMBER','OCTOBER','NOVEMBER','DECEMBER')")
    @MonthEnum
    public EnumSet<Month> months;

    @Type(type="models.IntegerSetUserType")
    @IntegerSet(min=-30,max=30)
    @Column(name="mday",columnDefinition="SET('1','2','3','4','5','6','7','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31','-1','-2','-3','-4','-5','-6','-7','-8','-9')")
    public Set<Integer> mdays;

    @Type(type="models.EnumSetUserType", parameters = @Parameter(name="enumType", value="models.Schedule$Weekday"))
    @Column(name="wday", columnDefinition="SET('MONDAY','TUESDAY','WEDNESDAY','THURSDAY','FRIDAY','SATURDAY')")
    @WeekdayEnum
    public EnumSet<Weekday> weekdays;

    @Type(type="models.IntegerSetUserType")
    @IntegerSet(min=0,max=23)
    @Column(name="hour",columnDefinition="SET('0','1','2','3','4','5','6','7','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23')")
    public Set<Integer> hours;

    @Type(type="models.IntegerSetUserType")
    @IntegerSet(min=0,max=59)
    @Column(name="minute",columnDefinition="SET('00','01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35','36','37','38','39','40','41','42','43','44','45','46','47','48','49','50','51','52','53','54','55','56','57','58','59')")
    public Set<Integer> minutes;

    public static Finder<Long,Schedule> find = new Finder<Long,Schedule>(Long.class, Schedule.class);

}

EnumSetUserType

public class EnumSetUserType<E extends Enum<E>> implements UserType, ParameterizedType, Serializable {
    private Class<? extends EnumSet> clazz = null;
    private Class<E> enum_type = null;

    @Override
    public void setParameterValues(Properties parameters) {
        String enum_class_name = parameters.getProperty("enumType");
        try {
            enum_type = ReflectHelper.classForName(parameters.getProperty("enumType"), this.getClass()).asSubclass(Enum.class);
            //enum_type = (Class<E>) Class.forName(enum_class_name);
            //enum_type = (Class<E>) Play.application().classloader().loadClass(enum_class_name);
        }
        catch (ClassNotFoundException e) {
            throw new HibernateException("enum class " + enum_class_name + " not found", e);
        }
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] column_names, SessionImplementor session, Object owner) throws HibernateException, SQLException {
        String value_str = rs.getString(column_names[0]);
        System.out.println("getting " + column_names[0] + " using " + getClass());
        if (rs.wasNull())
            return null;

        List<E> enum_values = new ArrayList<E>();
        for (String value : value_str.split(","))
            enum_values.add(Enum.valueOf(enum_type, value));

        return EnumSet.copyOf(enum_values);
    }

    @Override
    public void nullSafeSet(PreparedStatement statement, Object object, int index, SessionImplementor session) throws HibernateException, SQLException {
        System.out.println("Setting " + index + " to " + object + " using " + getClass());
        if (object == null) {
            statement.setNull(index, Types.VARCHAR);
            return;
        }

        Set<E> values = (Set<E>) object;
        StringBuilder sb = new StringBuilder();
        for (E value : values)
            sb.append(value.name()).append(",");

        System.out.println("Setting " + index + " to " + sb.length() + " using " + getClass());
        statement.setString(index, sb.substring(0, sb.length() - 1));
    }

    @Override
    public int[] sqlTypes() {
        return new int[] { Types.VARCHAR };
    }

    @Override
    public Class returnedClass() {
        return clazz;
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    @Override
    public boolean isMutable() {
        return false;
    }

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return original;
    }

    @Override
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return cached;
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) value;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        if (x == y)
            return true;

        if (x == null || y == null)
            return false;

        return x.equals(y);
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }

}

它不再报告任何错误,但即使我手动填充并保存Schedule在控制器操作中,Set字段也不会保存在数据库中。我怎样才能让 Play+Ebean 与这张桌子一起工作?

4

1 回答 1

0

如果将ENUM列映射到 Java 枚举,则可能应该将SET列映射到 Java EnumSet。SF上至少有一个关于 mapping的问题EnumSet,但那里的解决方案似乎是一个单独的表,而不是 mysqlSET类型。

hybernate 似乎也不支持 mysqlSET类型。因此,您必须编写自己的UserType. 我不确定有什么区别,但是您似乎可以轻松地将其设为EnhancedUserType,这可能会使您的实现更加通用。如果您的项目允许使用 LGPL 许可的源代码,您可以使用的实现作为EnumType您自己的模板EnumSetType。调整它应该很容易,尤其是当您可以丢弃代码的所有“另存为序数”部分时。

一旦有了自己的UserTypefor EnumSet,就可以用它来注释相应的字段@Type。或者在休眠配置中,如果您的设置中有这样的事情。甚至可能有办法在某处注册类型,自动映射EnumSet使用该类型的所有实例,但我对整个休眠的东西知之甚少,无法决定这是否可取甚至可能。我什至还没有弄清楚@Enumerated注释如何映射到EnumType实现。

使用正确的关键字 ( UserType EnumSet split),可以在网络上找到一些实现。因此,您甚至不必编写自己的代码,而只需包含这些解决方案之一即可。有些附有关于如何使用它们的简短说明。

于 2012-07-02T13:28:10.580 回答