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); }
...チェッ。