前準備

Entity の自動生成

  1. プロジェクトの対象サーバを設定
    jee1.png
  2. データベースからエンティティを作成
    jee2.png
  3. 新しいデータソース を選択して、先ほど作った MySQL への接続を選ぶ (← 本来は Glassfish の Connection Pool を指定すれば良いはずだが、Netbeans 7.3 では Glassfish の Connection Pool を指定するとデータベース情報が読み取られない)
    jee3.png
  4. テーブルが読み取られた
    jee4.png
  5. Order テーブルは、Order1 クラスになるのね ("Order by" とかぶるからだろう)
    jee5.png
  6. 終了
    jee6.png
  7. Entity クラス と persistence.xml が生成された
    jee7.png
    なんか、pom.xml も勝手にいじってくれて eclipse-link が provide 属性で登録されている
     
  8. DataSource? を Glassfish に定義されているものに変更する
    • Glassfish の Connection Pool を指定して、うまく Entity クラスが自動生成されれば必要ない作業。workround で作った jdbc/warehouse でテーブル情報を読み取ったので、jdbc/warehouse を使うような設定で自動生成されている。
       
    • persistence.xml のデータソースを Glassfish の Datasource に変更する
      jee8.png
       
      <?xml version="1.0" encoding="UTF-8"?>
      <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
          <persistence-unit name="warehousePU" transaction-type="JTA">
              <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
              <jta-data-source>jdbc/MySQLDataSource</jta-data-source>
              <class>com.mycompany.entity.Customer</class>
              <class>com.mycompany.entity.Item</class>
              <class>com.mycompany.entity.Order1</class>
              <exclude-unlisted-classes>false</exclude-unlisted-classes>
              <properties/>
          </persistence-unit>
      </persistence>
      
    • glassfish-resources.xml に、jdbc://warehouse の内容が定義されているので削除する
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN"
       "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
      <resources>
      </resources>
      

自動生成されたコードの内容

テーブル構造

database.png
CREATE  TABLE `warehouse`.`customer` (
  `id` BIGINT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(255) NOT NULL ,
  `address` VARCHAR(1024) ,
  PRIMARY KEY (`id`) );
 
CREATE  TABLE `warehouse`.`item` (
  `id` BIGINT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(255) NOT NULL ,
  `price` BIGINT ,
  `stock` BIGINT ,
  PRIMARY KEY (`id`) );
 
CREATE  TABLE `warehouse`.`order` (
  `id` BIGINT NOT NULL AUTO_INCREMENT ,
  `customer_id` BIGINT NOT NULL ,
  `item_id` BIGINT NOT NULL ,
  `amount` BIGINT NOT NULL,
  `shipdate` DATETIME NOT NULL,
  PRIMARY KEY (`id`),
  FOREIGN KEY (`customer_id`) references customer(`id`),
  FOREIGN KEY (`item_id`) references item(`id`) );

Customer.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.mycompany.entity;

import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

/**
 * Customer Table
 * @author atsushi
 */
@Entity
@Table(name = "customer")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Customer.findAll", query = "SELECT c FROM Customer c"),
    @NamedQuery(name = "Customer.findById", query = "SELECT c FROM Customer c WHERE c.id = :id"),
    @NamedQuery(name = "Customer.findByName", query = "SELECT c FROM Customer c WHERE c.name = :name"),
    @NamedQuery(name = "Customer.findByAddress", query = "SELECT c FROM Customer c WHERE c.address = :address")})
public class Customer implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Long id;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 255)
    @Column(name = "name")
    private String name;
    @Size(max = 1024)
    @Column(name = "address")
    private String address;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "customerId")
    private Collection<Order1> order1Collection;

    public Customer() {
    }

    public Customer(Long id) {
        this.id = id;
    }

    public Customer(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @XmlTransient
    public Collection<Order1> getOrder1Collection() {
        return order1Collection;
    }

    public void setOrder1Collection(Collection<Order1> order1Collection) {
        this.order1Collection = order1Collection;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Customer)) {
            return false;
        }
        Customer other = (Customer) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "com.mycompany.entity.Customer[ id=" + id + " ]";
    }
    
}

レコード ⇔ Entity Class

カラム ⇔ Field変数

PK


リレーション (1:n)

Item.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.mycompany.entity;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.Collection;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

/**
 *
 * @author atsushi
 */
@Entity
@Table(name = "item")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Item.findAll", query = "SELECT i FROM Item i"),
    @NamedQuery(name = "Item.findById", query = "SELECT i FROM Item i WHERE i.id = :id"),
    @NamedQuery(name = "Item.findByName", query = "SELECT i FROM Item i WHERE i.name = :name"),
    @NamedQuery(name = "Item.findByPrice", query = "SELECT i FROM Item i WHERE i.price = :price"),
    @NamedQuery(name = "Item.findByStock", query = "SELECT i FROM Item i WHERE i.stock = :stock")})
public class Item implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Long id;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 255)
    @Column(name = "name")
    private String name;
    @Column(name = "price")
    private BigInteger price;
    @Column(name = "stock")
    private BigInteger stock;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "itemId")
    private Collection<Order1> order1Collection;

    public Item() {
    }

    public Item(Long id) {
        this.id = id;
    }

    public Item(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigInteger getPrice() {
        return price;
    }

    public void setPrice(BigInteger price) {
        this.price = price;
    }

    public BigInteger getStock() {
        return stock;
    }

    public void setStock(BigInteger stock) {
        this.stock = stock;
    }

    @XmlTransient
    public Collection<Order1> getOrder1Collection() {
        return order1Collection;
    }

    public void setOrder1Collection(Collection<Order1> order1Collection) {
        this.order1Collection = order1Collection;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Item)) {
            return false;
        }
        Item other = (Item) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "com.mycompany.entity.Item[ id=" + id + " ]";
    }
    
}

Order1.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.mycompany.entity;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import javax.xml.bind.annotation.XmlRootElement;

/**
 *
 * @author atsushi
 */
@Entity
@Table(name = "order")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Order1.findAll", query = "SELECT o FROM Order1 o"),
    @NamedQuery(name = "Order1.findById", query = "SELECT o FROM Order1 o WHERE o.id = :id"),
    @NamedQuery(name = "Order1.findByAmount", query = "SELECT o FROM Order1 o WHERE o.amount = :amount"),
    @NamedQuery(name = "Order1.findByShipdate", query = "SELECT o FROM Order1 o WHERE o.shipdate = :shipdate")})
public class Order1 implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Long id;
    @Basic(optional = false)
    @NotNull
    @Column(name = "amount")
    private long amount;
    @Basic(optional = false)
    @NotNull
    @Column(name = "shipdate")
    @Temporal(TemporalType.TIMESTAMP)
    private Date shipdate;
    @JoinColumn(name = "item_id", referencedColumnName = "id")
    @ManyToOne(optional = false)
    private Item itemId;
    @JoinColumn(name = "customer_id", referencedColumnName = "id")
    @ManyToOne(optional = false)
    private Customer customerId;

    public Order1() {
    }

    public Order1(Long id) {
        this.id = id;
    }

    public Order1(Long id, long amount, Date shipdate) {
        this.id = id;
        this.amount = amount;
        this.shipdate = shipdate;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public long getAmount() {
        return amount;
    }

    public void setAmount(long amount) {
        this.amount = amount;
    }

    public Date getShipdate() {
        return shipdate;
    }

    public void setShipdate(Date shipdate) {
        this.shipdate = shipdate;
    }

    public Item getItemId() {
        return itemId;
    }

    public void setItemId(Item itemId) {
        this.itemId = itemId;
    }

    public Customer getCustomerId() {
        return customerId;
    }

    public void setCustomerId(Customer customerId) {
        this.customerId = customerId;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Order1)) {
            return false;
        }
        Order1 other = (Order1) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "com.mycompany.entity.Order1[ id=" + id + " ]";
    }
    
}

リレーション (n:1)

時刻型へのマッピング

その他の機能

複合PK

RDB と関係しない項目

JAXB

コード値

public enum STATE {
  ON,
  OFF,
  ACTIVE,
  PASSIVE
}

@Entity
public class Sensor {
  @Id
  private long id;
  @Enumerated
  priavte STATE state;
}

リレーションまとめ

キャッシュ (☆必ず無効化する)

@Entity
@Cacheable(true)
public class Item {
...
}

で、Entity が EntityManager? 内にキャッシュされるようになる

Unit Test で動かす

  1. テスト用のディレクトリを作る
    test1.png
  2. プロジェクト View からはこう見える (一度 [実行]-[プロジェクトをビルド] をする必要があるかも知れない)
    test2.png
  3. pom.xml に junit と derby への依存関係を追記する
        <dependencies>
        ...
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.derby</groupId>
                <artifactId>derby</artifactId>
                <version>10.9.1.0</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
  4. テスト用の persistence.xml を準備する
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
      <persistence-unit name="warehouseTestPU" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>com.mycompany.entity.Customer</class>
        <class>com.mycompany.entity.Item</class>
        <class>com.mycompany.entity.Order1</class>
        <properties>
          <property name="eclipselink.target-database" value="Derby"/>
          <property name="javax.persistence.jdbc.driber" value="org.apache.derby.jdbc.ClientDriver"/>
          <property name="javax.persistence.jdbc.url" value="jdbc:derby:/tmp/testdb;create=true"/>
          <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
          <!-- OFF,SEVERE,WARNING,INFO,CONFIG,FINE,FINER,FINEST,ALL -->
          <property name="eclipselink.logging.level" value="FINE" />
          <property name="eclipselink.logging.timestamp" value="false" />
          <property name="eclipselink.logging.session" value="false" />
          <property name="eclipselink.logging.thread" value="false" />
        </properties>
      </persistence-unit>
    </persistence>
    
    • Derby の組み込み DB
    • /tmp/testdb にデータベースファイルを造り、実行時にはテーブルを削除
    • Entity Class からテーブルを作る
    • eclipselink のログ設定は、persistence.xml に定義する。level を FINE にすると、発行する SQL 文まで見える
  5. JAXB のマッピング用に jaxb.index ファイルを作る (JAXB で XML⇔Object 変換を行うためには、パッケージ配下に ObjectFactory? か jaxb.index ファイルが必要)
    Customer
    Item
    Order1
  6. テストデータ Index1.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <item>
        <name>こんぼう</name>
        <stock>255</stock>
        <price>100</price>
    </item>
    
  7. Unit Test (ItemTest?.java)
    /*
     * To change this template, choose Tools | Templates
     * and open the template in the editor.
     */
    package com.mycompany.test.entity;
    
    import com.mycompany.entity.Item;
    import java.io.File;
    import java.math.BigInteger;
    import java.util.List;
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.EntityTransaction;
    import javax.persistence.Persistence;
    import javax.persistence.Query;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBException;
    import javax.xml.bind.Unmarshaller;
    import org.junit.BeforeClass;
    import org.junit.Test;
    import static org.junit.Assert.assertThat;
    import static org.hamcrest.CoreMatchers.is;
    import static org.hamcrest.CoreMatchers.equalTo;
    
    /**
     *
     * @author atsushi
     */
    public class ItemTest {
        
        private static JAXBContext jaxbContext;
        
        @BeforeClass
        public static void setUpClass() throws JAXBException {
            jaxbContext = JAXBContext.newInstance("com.mycompany.entity");
        }
        
        @Test
        public void testCreateItem() throws JAXBException {
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("warehouseTestPU");
            EntityManager em = emf.createEntityManager();
            EntityTransaction tx = em.getTransaction();
    
            // JAXBでXMLを読み込んでItemを作成し、RDBに登録
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            Item item1 = (Item) unmarshaller.unmarshal(new File("src/test/resources/item1.xml"));
    
            tx.begin();
            em.persist(item1);
            tx.commit();
    
            // 全件検索
            Query query = em.createNamedQuery("Item.findAll");
            List<Item> items = query.getResultList();
            
            // 検索結果の検証
            assertThat(items.size(), is(equalTo(1)));
            assertThat(items.get(0).getId(), is(equalTo(1L))); // Auto Incremet
            assertThat(items.get(0).getName(), is(equalTo("こんぼう")));
            assertThat(items.get(0).getPrice(), is(equalTo(new BigInteger("100"))));
            assertThat(items.get(0).getStock(), is(equalTo(new BigInteger("255"))));
            
            em.close();
            emf.close();
        }
        
    }
    
  8. 実行結果
    test3.png
  9. リファクタリング
    • price と stock が BigInteger? だと扱いにくいので int に変更する → 再テストをして合格なので OK
    • ... とは厳密には言えないんだよなぁ ... Unit Test は Derby で、本番環境は MySQL。MySQL で動くことは証明できない
    • 手軽に手元でテストできるというメリットと、本番環境と違うというデメリットを勘案する必要あり
      • 松案 Unit Test 用に開発者分のインスタンスを準備する (そうしないと同時実行できない)
      • 竹案 開発者端末にデータベースを準備する
      • 梅案 開発者端末での Unit Test は、Derby でやる。ただし Jenkins で定時テストをする際には MySQL で行う
    • MySQL ならまだ良いけど、Oracle とか DB2 とかだと費用的な問題でもっと話はやっかいになる。XE(Express Edition) を使うという手もあるけど、有象無象のサポートをやることを考えると梅案も捨てがたい

EntityManager?

JPQL

基本的に、Entity クラスに名前付き Query として定義して、Entity クラスのテストクラスで単体テストする。

JPQL の使い方

Query query = EntityManager#create***Query( … );
query.setParameter("city", req.getName());
query.setParameter("deliveryFrom", req.getFrom());
query.setParameter("deliveryTo", req.getTo());
query.setLockMode( … );
Item item = query.getSingleResult();

Query の作成

オススメEntity Manager のメソッド概要
×Queryem.createNamedQuery?(String name)名前付き Query を指定
TypedQuery?<T>em.createNamedQuery?(String name, Class<T> resultClass)名前付き Query を指定。返値の型指定版。JPQLはEntity の持ち物にして、業務ロジック内に書かない方が後々不幸を招かないと思う
×Queryem.createNativeQuery?(String sql)sql文指定
Queryem.createNativeQuery?(String sql, Class<T> resultClass)sql文指定。返値の型指定版。PL/SQL 呼び出しなど
×Queryem.createQuery(String jpql)jpql直接指定
TypedQuery?<T>em.createQuery(String jpql, Class<T> resultClass)jpql直接指定。返値の型指定版。どうしても名前付き Query を使えないときに使う。業務ロジック内で WHERE 句に指定する項目を動的に変更しなければいけないときなど
×TypedQuery?<T>em.createQuery(CriterialQuery?<T> criterialQuery)Criterial API を利用したクエリ。可読性悪し

名前付き Query

@Entity
@Table(name = "order")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Order1.findAll", query = "SELECT o FROM Order1 o"),
    @NamedQuery(name = "Order1.findById", query = "SELECT o FROM Order1 o WHERE o.id = :id"),
    @NamedQuery(name = "Order1.findByAmount", query = "SELECT o FROM Order1 o WHERE o.amount = :amount"),
    @NamedQuery(name = "Order1.findByShipdate", query = "SELECT o FROM Order1 o WHERE o.shipdate = :shipdate")})
public class Order1 implements Serializable {
  public static final String FIND_ALL = "Order1.findAll";
  public static final String FIND_BY_ID = "Order1.findById";
  public static final String FIND_BY_AMOUNT = "Order1.findByAmount";
  public static final String FIND_BY_SHIPDATE = "Order1.findByShipdate";
  ...

SELECT文

SELECT NEW

CASE句 / 集計関数 / スカラ式

副問い合わせ

SELECT c
FROM Customer c
WHERE c.creditLimit < (SELECT SUM(o.price) FROM Order1 o WHERE o.customer = c)

受注額が与信枠を超えている顧客の一覧。
JPQL副問い合わせの WHERE 句が Object 同士の比較なことに注意。発行される SQL 文は、order.customerId = customer.id とか FK,PK の比較に展開される。

WHERE句

DELETE文 (一括削除)

DELETE FROM Order1 o
WHERE o.shipdate < :date

ある日付以前の受注を削除

UPDATE文 (一括更新)

UPDATE Customer c
SET c.creditLimit = 1000000
WHERE c.group = :group

指定グループの顧客の与信枠を 100万円にする

埋め込みパラメータの直接比較はできない

SELECT *
FROM User u 
WHERE
  (:name = "" OR u.name = :name)
  AND (:sex = "" OR u.sex = :sex)
  AND (:age = "" OR u.age = :age)

Lock

楽観ロック

悲観ロック

Event

JPAのためのテーブル設計

CREATE  TABLE `warehouse`.`customer_table` (
  `id` BIGINT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(255) NOT NULL ,
  `address` VARCHAR(1024) ,
  `version` BIGINT ,
  PRIMARY KEY (`id`) );
    
CREATE  TABLE `warehouse`.`item_table` (
  `id` BIGINT NOT NULL AUTO_INCREMENT ,
  `jancode` INT NOT NULL ,
  `name` VARCHAR(255) NOT NULL ,
  `price` BIGINT ,
  `stock` BIGINT ,
  `version` BIGINT ,
  INDEX INDEX_ORDER (`jancode`),
  PRIMARY KEY (`id`) );
    
CREATE  TABLE `warehouse`.`order_table` (
  `id` BIGINT NOT NULL AUTO_INCREMENT ,
  `customer_id` BIGINT NOT NULL ,
  `item_id` BIGINT NOT NULL,
  `amount` BIGINT NOT NULL,
  `shipdate` DATETIME NOT NULL,
  `version` BIGINT,
  PRIMARY KEY (`id`),
  FOREIGN KEY (`customer_id`) references customer_table(`id`),
  FOREIGN KEY (`item_id`) references item_table(`id`) );

Java#Glassfish


添付ファイル: filecache.png 467件 [詳細] filetest3.png 439件 [詳細] filetest2.png 461件 [詳細] filetest1.png 459件 [詳細] filedatabase.png 463件 [詳細] filejee8.png 443件 [詳細] filejee7.png 477件 [詳細] filejee6.png 484件 [詳細] filejee5.png 465件 [詳細] filejee4.png 453件 [詳細] filejee3.png 457件 [詳細] filejee2.png 459件 [詳細] filejee1.png 475件 [詳細] filemysql4.png 487件 [詳細] filemysql3.png 455件 [詳細] filemysql2.png 475件 [詳細] filemysql1.png 473件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS   sitemap
Last-modified: 2014-02-27 (木) 01:30:25 (1011d)
ISBN10
ISBN13
9784061426061