読者です 読者をやめる 読者になる 読者になる

この日記は私的なものであり所属会社の見解とは無関係です。 GitHub: takahashikzn

MySQLのPacketTooBigException

EclipseLink

MySQLへデカいファイルを送るときは、my.cnf(またはmy.ini)で

max_allowed_packet=10M

のように設定する。みんな知ってるね。

しかし。。。

では10Mまでのパケットを送れるように設定したのだから、
9.5Mのファイルを転送しても当然OKだろ常識的に考えて。
と思ってやってみたのだが、com.mysql.jdbc.PacketTooBigExceptionが発生。


ちなみに、INSERTしようとしているエンティティはごくフツー。こんな感じ。

@Entity
public class BinaryDataEntity {

    @Column
    @Lob
    private byte[] data;

    @Column
    private String name;

    ...
}

なぜ???

コレは予想だが。。。

byte[]フィールドを永続化しようとしたとき、EclipseLinkは


PreparedStatement#setBytes


を呼ぶんだろう。
そして、connector-JはどうやらバイナリデータをBASE64か何かの文字列にして
MySQLへ送っているっぽい。
で、転送データサイズが2〜3倍にふくれあがっている、と。
そりゃあかんわな。


というわけで、max_allowed_packetのサイズを64Mに設定にして解決したのでした。

もしかすると

@Lobなフィールドをbyte[]じゃなくてInputStreamとして宣言すると、
EclipseLinkは


PreparedStatement#setInputStream


を使ってくれる...かも。
そうだとすると、バイナリデータを文字列にせずそのまま転送してくれるようになる...かも。

@Entity
public class BinaryDataEntity {

    @Column
    @Lob
    private InputStream data;

    @Column
    private String name;

    ...
}


ただ、EclipseLink-1.xの時代は、確か@LobはInputStreamに対応していなかったはず。
EclipseLink-2.xになってから改善されているのかどうかはシラン。ちょっとやってみよう。


ちなみにOpenJPAの場合は

@org.apache.openjpa.persistence.Persistent

を付ければInputStream、OK!なはず。


というわけで、やってみたが

現時点における最新版であるEclipseLink-2.1.1ではどうやら、
InputStreamを使うのは無理っぽい。


org.eclipse.persistence.internal.jpa.metadata.converters.LobMetadata


で、@Lobとして扱える型であるかチェックしているのだが、そこでエラーになる。

Caused by: Exception [EclipseLink-7164] (Eclipse Persistence Services - 2.1.1.v20100817-r8050): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The type [class java.io.InputStream] for the attribute [data] on the entity class [class jp.root42.r42fw.model.entity.AbstractBinaryDataEntity] is not a valid type for a lob mapping. For a lob of type BLOB, the attribute must be defined as a java.sql.Blob, byte[], Byte[] or a Serializable type. For a lob of type CLOB, the attribute must be defined as a java.sql.Clob, char[], Character[] or String type.
	at org.eclipse.persistence.exceptions.ValidationException.invalidTypeForLOBAttribute(ValidationException.java:1105)
	at org.eclipse.persistence.internal.jpa.metadata.converters.LobMetadata.process(LobMetadata.java:114)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.processLob(MappingAccessor.java:1443)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.BasicAccessor.processLob(BasicAccessor.java:443)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.processJPAConverters(MappingAccessor.java:1429)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.processMappingConverter(MappingAccessor.java:1495)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor.processMappingValueConverter(MappingAccessor.java:1513)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.BasicAccessor.process(BasicAccessor.java:367)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataDescriptor.processAccessors(MetadataDescriptor.java:1365)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.ClassAccessor.processAccessors(ClassAccessor.java:963)
	at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.MappedSuperclassAccessor.processMetamodelDescriptor(MappedSuperclassAccessor.java:1131)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processStage2(MetadataProject.java:1522)
	at org.eclipse.persistence.internal.jpa.metadata.MetadataProcessor.processORMMetadata(MetadataProcessor.java:474)
	at org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor.processORMetadata(PersistenceUnitProcessor.java:441)
	at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:959)
	... 32 more


該当箇所のソースは次の通り。

(LobMetadataの70行目あたり)

    /**
     * INTERNAL:
     * Returns true if the given class is a valid blob type.
     */ 
    public static boolean isValidBlobType(MetadataClass cls) {
        return cls.equals(byte[].class) ||
               cls.equals(Byte[].class) ||
               cls.equals(java.sql.Blob.class);
    }

...チェッ。