Hatena::Groupseasarproject

S2できるかな?

2013-03-05

Java SE 6と7では、S2JDBC-Genで作られるDDLのスキーマが違う。#2

g:seasarproject:id:halflite:20120626:s2jdbcgen の続きです。*1 これのために、JDK 7に移行するのを躊躇してたんですが、JDK 6もEoLですし*2、これを機会に、正しく向き合おうかと…。

例えば、こんな二つのクラスで、エンティティを作ります。

/** 更新可能entityの抽象クラス */
@MappedSuperclass
public abstract class AbstractUpdatableEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    /** ID */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long id;

    /** 変更時間 */
    @Column(nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    public Date modified;

    /** 作成時間 */
    @Column(updatable = false, nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    public Date created;

    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}
/** ユーザー変更一時情報 */
@Entity
public class TemporaryUser extends AbstractUpdatableEntity {

	private static final long serialVersionUID = 1L;

	/** ユーザー ID */
	@Column(nullable = false)
	public Long userId;	
	
	/** メールアドレス */
	@Column(length = MAIL_MAX, nullable = false)
	public String mail;

	/** 変更種別 */
	@Column(length = 32, nullable = false)
	@Enumerated(EnumType.STRING)
	public TemporaryUserType type;
	
	@OneToOne
	@ReferentialConstraint(onDelete = ReferentialActionType.CASCADE)
	public User user;

	// -------------------------------------------------- [inner class]

	public static enum TemporaryUserType {
		/** 新規登録時 */
		REGISTERED,
		/** パスワード再発行時 */
		PASSWORD_REISSUED,
		/** メールアドレス変更時 */
		MAIL_CHANGED;
	}
}

S2JDBC-Gengen-ddlでcreate table文を作成。 JDK 6の場合は、以下のSQLが作られます。

create table TEMPORARY_USER (
    ID bigint not null auto_increment comment 'ID',
    USER_ID bigint not null comment 'ユーザー ID',
    MAIL varchar(255) not null comment 'メールアドレス',
    TYPE varchar(32) not null comment '変更種別',
    MODIFIED timestamp not null comment '変更時間',
    CREATED timestamp not null comment '作成時間',
    constraint TEMPORARY_USER_PK primary key(ID)
) comment = 'ユーザー変更一時情報';

しかし、JDK 7で実行した場合の、SQLは以下のようになります。

create table TEMPORARY_USER (
    ID bigint not null auto_increment comment 'ID',
    CREATED timestamp not null comment '作成時間',
    MODIFIED timestamp not null comment '変更時間',
    TYPE varchar(32) not null comment '変更種別',
    MAIL varchar(255) not null comment 'メールアドレス',
    USER_ID bigint not null comment 'ユーザー ID',
    constraint TEMPORARY_USER_PK primary key(ID)
) comment = 'ユーザー変更一時情報';

はい、ID以下のカラムの順番が、丁度逆になってますね…。

別にRDBだから、カラムの並び順とか、オブジェクトにマッピングされてしまえば関係ないのかもしれませんが、これはどうにも座りが悪い…。

Java SE 6と7では、S2JDBC-Genで作られるDDLのスキーマが違う。#3

g:seasarproject:id:halflite:20130305:s2jdbcgen からの続き。

カラムの並び替えは、

org.seasar.extension.jdbc.gen.internal.desc.TableDescFactoryImplクラス

のdoColumnDescメソッドで行っているのですが、そこを

差し替えればできます。

[Seasar-user:17896] Re: S2JDBC-Gen の gen-ddl が生成する DDL での主キーのカラム順について

をヒントに、S2JDBC-Genのソースを落としてきて、Eclipseにインポート、該当のクラスのテストケースを探します。*3

    /**
     * 
     * @throws Exception
     */
    @Test
    public void testColumnDescList_multiClass() throws Exception {
        EntityMeta entityMeta = entityMetaFactory.getEntityMeta(Ggg.class);
        TableDesc tableDesc = tableDescFactory.getTableDesc(entityMeta);
        List<ColumnDesc> list = tableDesc.getColumnDescList();
        assertEquals(6, list.size());
        assertEquals("ID1", list.get(0).getName());
        assertEquals("ID2", list.get(1).getName());
        assertEquals("ID3", list.get(2).getName());
        assertEquals("NAME3", list.get(3).getName());
        assertEquals("NAME2", list.get(4).getName());
        assertEquals("NAME1", list.get(5).getName());
    }

上記テストケース、JDK 6だと通るんですが、JDK 7だと通りません。*4

カラムを並べ替えているComparator実装が原因っぽいです。*5

    /**
     * カラム記述の{@link Comparator}を作成します。
     * 
     * @param tableDesc
     *            テーブル記述
     * @return カラム記述の{@link Comparator}
     */
    protected Comparator<ColumnDesc> createColumnDescComparator(
            TableDesc tableDesc) {
        final List<String> pkColumnNameList = new ArrayList<String>();
        if (tableDesc.getPrimaryKeyDesc() != null) {
            PrimaryKeyDesc primaryKeyDesc = tableDesc.getPrimaryKeyDesc();
            pkColumnNameList.addAll(primaryKeyDesc.getColumnNameList());
        }
        return new Comparator<ColumnDesc>() {

            public int compare(ColumnDesc o1, ColumnDesc o2) {
                int index1 = pkColumnNameList.indexOf(o1.getName());
                int index2 = pkColumnNameList.indexOf(o2.getName());
                int ret = 0;
                if (index1 < 0) {
                    if (index2 < 0) {
                        ret = -1;
                    } else {
                        ret = 1;
                    }
                } else {
                    if (index2 < 0) {
                        ret = -1;
                    } else {
                        ret = index1 - index2;
                    }
                }
                return ret;
            }
        };
    }

ret = ... で定数を返すと、JDK 6/JDK 7でソートのロジック変わったので、それによって、挙動も変わってしまうようです。*6

修正するには、TableDescFactoryImplクラスの、createColumnDescComparatorメソッドを上書きして、JARを作り直せばいいのですが、さて、どう直していいのか分からない…。