質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.97%

JPAでのPrimaryKeyが無くNULLのあるテーブルへのアクセスについて

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 6,280

hitokamu

score 5

JPAでのPrimaryKeyが無くNULLのあるテーブルへのアクセスについて

S2DAOで構築されたシステムをJPAで置き換えを試みています。  
テーブルの中にPrimaryKeyが無いものがあったのですが、稼働中のシステムの為データベースにPrimaryKeyを追加することはできません。
そこで全てのカラムをEmbeddableとして定義することとしたのですが、この方法ではデータの中に一つでもNULLが含まれる場合にDatabaseExceptionが発生してしました。
そもそもJPAではこのような使い方をすることが許されないのでしょうか?。

発生している問題・エラーメッセージ

selectAllでAAA_TBLに対しSELECTを実行しようとすると、
カラムBBB,CCCにnullのデータが無い状態ではSELECTの実行結果を
取得できるのですが、nullが1件でもあると
「※ここで例外」とコメントを付けた箇所でDatabaseExceptionが発生してしまいます。

エラーメッセージ
2016-09-06 19:59:14,832 [http-nio-8080-exec-16] FATAL (AAA.java:48) - javax.persistence.PersistenceException: Exception [EclipseLink-6044] (Eclipse Persistence Services - 2.6.3.v20160428-59c81c5): org.eclipse.persistence.exceptions.QueryException
Exception Description: The primary key read from the row [ArrayRecord(
    AAA_TBL.BBB => null
    AAA_TBL.CCC => null)] during the execution of the query was detected to be null.  Primary keys must not contain null.
Query: ReadAllQuery(name="AAATbl.findAll" referenceClass=AAATbl sql="SELECT BBB, CCC FROM AAA_TBL")

###該当のソースコード
@Entity
@Table(name = "AAA_TBL")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "AAA.findAll", query = "SELECT c FROM AAATbl c")})
public class AAATbl implements Serializable {

    private static final long serialVersionUID = 1L;

    @EmbeddedId
    protected AAATblPK aaaTblPK;

    public AAATbl() {
    }

    public AAATbl(AAATblPK aaaTblPK) {
        this.aaaTblPK= aaaTblPK;
    }

    public AAATbl(String bbb,String ccc) {
        this.aaaTblPK= new AAATblPK(bbb,ccc);
    }

    public AAATblPK getAAATblPK() {
        return this.aaaTblPK;
    }

    public void setAAATblPK(AAATblPK aaaTblPK) {
        this.aaaTblPK= aaaTblPK;
    }
  public class AAATblPK implements Serializable {

    @Size(max = 1 )
    @Column(name = "BBB")
    private String bbb;
    @Size(max = 1 )
    @Column(name = "CCC")
    private String ccc;
    public AAAPK() {
    }
    public AAAPK(String bbb,String ccc) {
        this.bbb = bbb;
        this.ccc = ccc;
    }
    public String getBbb() {
        return bbb;
    }

    public void setBbb(String bbb) {
        this.bbb = bbb;
    }
    public String getCcc() {
        return ccc;
    }

    public void setCcc(String Ccc) {
        this.ccc = ccc;
    }
  @Override
    public int hashCode() {
        int hash = 0;
        hash += (bbb != null ? bbb.hashCode() : 0);
        hash += (ccc != null ? ccc.hashCode() : 0);

        return hash;
    }

    @Override
    public boolean equals(Object object) {
        if (!(object instanceof AAATblPK)) {
            return false;
        }
        AAATblPK other = (AAATblPK) object;
        if ((this.bbb== null && other.bbb!= null) || (this.bbb!= null && !this.bbb.equals(other.bbb))) {
            return false;
        }
        if ((this.ccc== null && other.ccc!= null) || (this.ccc!= null && !this.ccc.equals(other.ccc))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "tmp.AAATblPK[ bbb= "+  bbb + ",ccc=" + ccc +" ]";
    }
}


    public AAATbl[] selectAll() {
        try {

            Query query;
            query = em.createNamedQuery("AAATbl.findAll");
            List<AAATbl> list;
            list = query.getResultList();    // ※ここで例外
            AAATbl[] ret;
            ret = (AAATbl[]) list.toArray(new AAATbl[list.size()]);
            return ret;
        } catch (NoResultException e) {
            return null;
        } catch (PersistenceException | DatabaseException databaseEx) {
            logger.fatal(e);
            return null;
        } catch (Exception e) {
            logger.fatal(e);
            return null;
        }
    }

 テーブル構造

実際のカラムは32個ありますが、"BBB","CCC"の2つに省略化して記載しています。

CREATE TABLE "DATABASE"."AAA_TBL" 
   (    "BBB" VARCHAR2(1 BYTE), 
        "CCC" VARCHAR2(1 BYTE)
) SEGMENT CREATION IMMEDIATE 
  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS NOLOGGING
  STORAGE(INITIAL 131072 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "USERS" ;

試したこと

テーブルの値にNULL値が含まれない場合は値を取得できる事を確認しています。
@PrimaryKey(validation=IdValidation.NONE)を追加してもみたのですが、状況は変わりませんでした。

(2016/09/08追記)
最適解かどうかはわからないのですが、NamedQueryを使うのを諦め、NativeQueryでObject型とすればテーブルのデータを取り出せることは確認できました。

public AAATbl[] selectAll() {
        try {
            String sql = "SELECT BBB,CCC FROM AAA_TBL";
            List<AAATbl> dest = new ArrayList<>();
            List<Object[]> list = (List<Object[]>) em.createNativeQuery(sql).getResultList();
            Iterator it = list.iterator();
            while (it.hasNext()) {
                AAATbl data = new AAATbl();
                Object[] obj = (Object[]) it.next();
                int i = 0;
                data.bbb((String) obj[i++]);
                data.ccc((String) obj[i++])
                dest.add(data);
            }
            AAATbl[] ret;
            ret = (AAATbl[]) dest.toArray(new AAATbl[dest.size()]);
            return ret;
        } catch (NoResultException e) {
            return null;
        } catch (PersistenceException | DatabaseException databaseEx) {
            logger.fatal(databaseEx);
            return null;
        } catch (Exception e) {
            logger.fatal(e);
            return null;
        }
    }

補足情報(言語/FW/ツール等のバージョンなど)

環境はNetBeans(8.1)+TomEE(7.0.1)
JPAはEclipse Persistence Services - 2.6.3.v20160428-59c81c5を使用しています。
DataBaseは Oracle 11.2.0.1.0を使用しています。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • 退会済みユーザー

    退会済みユーザー

    2016/09/07 00:26

    TomcatEE ? TomEE の間違い?それともEE自体が間違い?

    キャンセル

  • hitokamu

    2016/09/07 13:23

    TomEEの誤記でしたので訂正しました。ご指摘ありがとうございます。

    キャンセル

回答 3

check解決した方法

0

createNativeQueryの引数にクラスを指定せず、Object型として取り出した後にListに詰めることでデータを取り出すことは可能になりました。
ソースコードは「試したこと」の2016/09/08に追記したものとなります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

column構成がわかれば解はでるきがするが
テーブル名はtable,システムを連想しそうなカラムにはcol01等にかえたcreate文をのせてみよう。

そのまままでもいいです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/07 13:48

    助言していただきありがとうございます。カラム名は質問した際のまま(BBB,CCC)ですが、テーブル構造の欄を追記しました。

    キャンセル

0

JPAではEntityBeanはDBのTableデザインと一致していることが前提となります。EntityBeanのメンバ定義とTableのcolumn定義が不一致の場合、何らかの不具合が起きても不思議はないです。PrimalyKeyにnullが含まれるというのはDBとして矛盾なので、エラーとして弾いている可能性が考えられます。ご質問に対する解としてはできないのではないかと思います。

DB側が変更できない事情のようなので、EntityBean側のPrimalyKeyアノテーションを外すことになるかと思います。同一事象に遭遇していないので、試したことはないのですが。


EntityBean側にPrimalyKey指定が有るものと勘違いしていました。済みません。
PrimalyKey指定していないので、値がnullでも問題ないように思えます。Oracle接続の環境が作れないのでちょっと調べられません。解決につながるか分かりませんが、エラー内容やサーバーログなど提示してみてはどうでしょうか。関係ないかも知れませんが、varcher2のサイズがTableとEntityで異なるのが気になりました。


EmbeddedIdアノテーションは複合主キーの指定となるようです。

複合主キーの定義方法

このためAAATblPKがPrimalyKeyとされ、nullでExceptionとなったものと思われます。
@EmbeddedIdを使用するのをやめるのと同時にメンバ指定をサブクラスで指定するのをやめ、AAATblに直接bbb、cccのメンバを記載されてはどうでしょうか。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/08 20:54

    了解です。回避策が有るようで何よりでした。

    キャンセル

  • 2016/09/08 21:10

    回避策をもって自己解決とされて良いかと思います。

    キャンセル

  • 2016/09/13 09:25

    助言いただきありがとうございます。回避策を自己解決とします。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.97%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる