0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Spring Boot×JpaRepository×MySQL】Hibernate Toolsで生成したVIEW由来のEntityクラスの使い方

Last updated at Posted at 2020-06-14

Spring BootとJpaRepositoryを使ってEclipseのHibernate Toolsで生成したVIEW由来のEntityクラスの使い方です。
使用するデータベースはMySQLです。
テーブル由来のEntityクラスと違って注意すべき点がいくつかあるので、そのことについてまとめました。

環境

  • OS:Windows10
  • IDE:Eclipse2020-03
  • Java:8
  • MySQL:5.7
  • Spring Boot:2.3.1
  • Hibernate ORM:5.4

VIEW由来のEntityクラス

基本的な内容

Hibernate Toolsで生成したVIEW由来のEntityクラスを使うには、

  1. 主キーにあたるカラムに@Idを追記する
  2. クラス名の上に@Immutableを追記する

必要があります。

主キーにあたるカラムがshop_idであるshop_informationsビュー由来のEntityクラスであるShopInformation.javaの場合は以下の通り。

ShopInformation.java

package jp.co.hibernate_exchange_with_mysql.domain.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

import org.hibernate.annotations.Immutable;


/**
 * The persistent class for the shop_informations database table.
 *
 */
@Entity
@Immutable // 追記
@Table(name="shop_informations")
@NamedQuery(name="ShopInformation.findAll", query="SELECT s FROM ShopInformation s")
public class ShopInformation implements Serializable {
	private static final long serialVersionUID = 1L;

	private String address;

	@Column(name="category_name")
	private String categoryName;

	@Id // 追記
	@Column(name="shop_id", updatable = false, nullable = false) // 追記(任意)
	private Integer shopId;

	@Column(name="shop_name")
	private String shopName;

	private String tel;

	@Column(name="zip_code")
	private String zipCode;

	public ShopInformation() {
	}

	public String getAddress() {
		return this.address;
	}

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

	public String getCategoryName() {
		return this.categoryName;
	}

	public void setCategoryName(String categoryName) {
		this.categoryName = categoryName;
	}

	public Integer getShopId() {
		return this.shopId;
	}

	public void setShopId(Integer shopId) {
		this.shopId = shopId;
	}

	public String getShopName() {
		return this.shopName;
	}

	public void setShopName(String shopName) {
		this.shopName = shopName;
	}

	public String getTel() {
		return this.tel;
	}

	public void setTel(String tel) {
		this.tel = tel;
	}

	public String getZipCode() {
		return this.zipCode;
	}

	public void setZipCode(String zipCode) {
		this.zipCode = zipCode;
	}

}

それぞれの項目について、解説していきます。

1. 主キーにあたるカラムに@Idを追記する

MySQLの場合、VIEWに主キーを定義できないため、Hibernate ORMでEntityクラスを生成した時点では@Idは付いていません(テーブルの場合は付与されている)。そのため、手動で追記してあげる必要があります。
この@Idを追記しないと、起動時に以下のようなエラーが出て落ちてしまいます。

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shopInformationRepository' defined in jp.co.hibernate_exchange_with_mysql.domain.repository.ShopInformationRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Cannot resolve reference to bean 'jpaMappingContext' while setting bean property 'mappingContext'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is org.hibernate.AnnotationException: No identifier specified for entity: jp.co.hibernate_exchange_with_mysql.domain.model.ShopInformation
・・・(以下省略)

2. クラス名の上に@Immutableを追記する

こちらは記載しなくても動きます。
しかし、VIEW上のデータは直接操作はできない(read only)ため、@Immutableをつけて変更不可とします。

なお、JpaRepositoryを継承したRepositoryインターフェースは以下の通り。

ShopInformationRepository.java

package jp.co.hibernate_exchange_with_mysql.domain.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import jp.co.hibernate_exchange_with_mysql.domain.model.ShopInformation;

@Repository
public interface ShopInformationRepository extends JpaRepository<ShopInformation, Integer>{
}

単一主キーのテーブルと同じく、extends JpaRepository<エンティティクラス, ID>のIDの部分には、主キーに当たるカラムのデータ型を書きます。

主キーにあたるカラムが複数存在する場合

VIEWに主キーにあたるカラムが複数存在する(複合主キーにあたる)場合は、上述の内容の他に、別途PKクラスを定義して、Entityクラス上で明示する必要があります。

例えば、主キーにあたるカラムがshop_id, item_id ,user_idのshop_itemsビュー由来のEntityクラスであるShopItem.javaの場合は以下の通り。

ShopItem.java

package jp.co.hibernate_exchange_with_mysql.domain.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

import org.hibernate.annotations.Immutable;


/**
 * The persistent class for the shop_items database table.
 *
 */
@Entity
@Immutable // 追記
@IdClass(ShopItemPK.class) // 追記(別途PKクラスを定義)
@Table(name="shop_items")
@NamedQuery(name="ShopItem.findAll", query="SELECT s FROM ShopItem s")
public class ShopItem implements Serializable {
	private static final long serialVersionUID = 1L;

	@Column(name="is_favorite")
	private Integer isFavorite;

	@Id // 追記
	@Column(name="item_id", updatable = false , nullable = false) // 追記(任意)
	private String itemId;

	@Column(name="item_name")
	private String itemName;

	private Integer price;

	@Id // 追記
	@Column(name="shop_id", updatable = false , nullable = false) // 追記(任意)
	private Integer shopId;

	@Column(name="shop_name")
	private String shopName;

	@Id // 追記
	@Column(name="user_id", updatable = false , nullable = false) // 追記(任意)
	private Integer userId;

	public ShopItem() {
	}

	public Integer getIsFavorite() {
		return this.isFavorite;
	}

	public void setIsFavorite(Integer isFavorite) {
		this.isFavorite = isFavorite;
	}

	public String getItemId() {
		return this.itemId;
	}

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

	public String getItemName() {
		return this.itemName;
	}

	public void setItemName(String itemName) {
		this.itemName = itemName;
	}

	public Integer getPrice() {
		return this.price;
	}

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

	public Integer getShopId() {
		return this.shopId;
	}

	public void setShopId(Integer shopId) {
		this.shopId = shopId;
	}

	public String getShopName() {
		return this.shopName;
	}

	public void setShopName(String shopName) {
		this.shopName = shopName;
	}

	public Integer getUserId() {
		return this.userId;
	}

	public void setUserId(Integer userId) {
		this.userId = userId;
	}

}

PK(プライマリーキー)クラスは以下の通りとなります。

ShopItemPK.java
package jp.co.hibernate_exchange_with_mysql.domain.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Embeddable;

/**
 * The primary key class for the user_favorites database table.
 *
 */
@Embeddable
public class ShopItemPK implements Serializable {
	//default serial version id, required for serializable classes.
	private static final long serialVersionUID = 1L;

	@Column(name="shop_id")
	private Integer shopId;

	@Column(name="item_id")
	private Integer itemId;

	@Column(name="user_id")
	private Integer userId;

	public ShopItemPK() {
	}
	public Integer getShopId() {
		return this.shopId;
	}
	public void setShopId(Integer shopId) {
		this.shopId = shopId;
	}
	public Integer getItemId() {
		return this.itemId;
	}
	public void setItemId(Integer itemId) {
		this.itemId = itemId;
	}
	public Integer getUserId() {
		return this.userId;
	}
	public void setUserId(Integer userId) {
		this.userId = userId;
	}

	public boolean equals(Object other) {
		if (this == other) {
			return true;
		}
		if (!(other instanceof ShopItemPK)) {
			return false;
		}
		ShopItemPK castOther = (ShopItemPK)other;
		return
			(this.shopId == castOther.shopId)
			&& (this.itemId == castOther.itemId)
			&& (this.userId == castOther.userId);
	}

	public int hashCode() {
		final int prime = 31;
		int hash = 17;
		hash = hash * prime + this.shopId;
		hash = hash * prime + this.itemId;
		hash = hash * prime + this.userId;

		return hash;
	}
}

主キーが数値型以外の場合は、hashCode()メソッド内の記述を以下のようにします(.hashCode()を使います。)。

hash = hash * prime + this.itemId.hashCode(); //itemIdがString型の場合

Repositoryインターフェースは以下の通り。

ShopItemRepository.java
package jp.co.hibernate_exchange_with_mysql.domain.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import jp.co.hibernate_exchange_with_mysql.domain.model.ShopItem;
import jp.co.hibernate_exchange_with_mysql.domain.model.ShopItemPK;

@Repository
public interface ShopItemRepository extends JpaRepository<ShopItem, ShopItemPK>{

}

extends JpaRepository<エンティティクラス, IDのIDの部分には、PKクラスを書きます(複合主キーを持つテーブルと同様)。

テーブル定義とVIEW定義

今回作成したEntityクラスのもととなるテーブルとVIEWの定義を以下に記載しておきます。

テーブル定義

shops
create table shops (
  shop_id INT not null AUTO_INCREMENT comment '店舗ID'
  , shop_name VARCHAR(100) comment '店舗名'
  , tel VARCHAR(15) comment '電話番号'
  , zip_code VARCHAR(8) comment '郵便番号'
  , address VARCHAR(150) comment '住所'
  , category_id SMALLINT comment 'カテゴリID'
  , created_by VARCHAR(100) comment '新規作成者'
  , created_at DATETIME default now() comment '新規作成日'
  , updated_by VARCHAR(100) comment '最終更新者'
  , updated_at DATETIME default now() comment '最終更新日'
  , is_deleted TINYINT default 0 comment '削除フラグ'
  , constraint shop_PKC primary key (shop_id)
)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin comment 'ショップ' ;
shop_categories
create table shop_categories (
  category_id SMALLINT not null AUTO_INCREMENT comment 'カテゴリID'
  , category_name VARCHAR(100) comment 'カテゴリ名'
  , created_by VARCHAR(100) comment '新規作成者'
  , created_at DATETIME default now() comment '新規作成日'
  , updated_by VARCHAR(100) comment '最終更新者'
  , updated_at DATETIME default now()comment '最終更新日'
  , is_deleted TINYINT default 0 comment '削除フラグ'
  , constraint shop_category_PKC primary key (category_id)
)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin comment 'ショップカテゴリ' ;
items
create table items (
  item_id  VARCHAR(43) not null comment '商品ID'
  , item_name VARCHAR(100) comment '商品名'
  , price INT comment '価格'
  , shop_id INT comment 'ショップID'
  , created_by VARCHAR(100) comment '新規作成者'
  , created_at DATETIME default now() comment '新規作成日'
  , updated_by VARCHAR(100) comment '最終更新者'
  , updated_at DATETIME default now() comment '最終更新日'
  , is_deleted TINYINT default 0 comment '削除フラグ'
  , constraint items_PKC primary key (item_id)
)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin comment '商品' ;
users
create table users (
  user_id INT not null AUTO_INCREMENT comment 'ユーザーID'
  , user_name VARCHAR(100) comment 'ユーザー名'
  , tel VARCHAR(15) comment '電話番号'
  , zip_code VARCHAR(8) comment '郵便番号'
  , address VARCHAR(150) comment '住所'
  , password VARCHAR(20) comment 'パスワード'
  , created_by VARCHAR(100) comment '新規作成者'
  , created_at DATETIME default now() comment '新規作成日'
  , updated_by VARCHAR(100) comment '最終更新者'
  , updated_at DATETIME default now() comment '最終更新日'
  , is_deleted TINYINT default 0 comment '削除フラグ'
  , constraint users_PKC primary key (user_id)
)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin comment 'ユーザー' ;
user_favorites
create table user_favorites (
  item_id INT not null comment '商品ID'
  , user_id INT not null comment 'ユーザーID'
  , created_by VARCHAR(100) comment '新規作成者'
  , created_at DATETIME default now() comment '新規作成日'
  , updated_by VARCHAR(100) comment '最終更新者'
  , updated_at DATETIME default now() comment '最終更新日'
  , is_deleted TINYINT default 0 comment '削除フラグ'
  , constraint user_favorites_PKC primary key (item_id,user_id)
)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin comment 'お気に入り' ;

VIEW定義

shop_informations
create view `shop_informations`
as select
sh.shop_id,
sh.shop_name,
sc.category_name,
sh.zip_code,
sh.address,
sh.tel
from shops sh left outer join shop_categories sc on sh.category_id = sc.category_id and sc.is_deleted = 0
where sh.is_deleted = 0;
shop_items
create view `shop_items`
as select
si.*,
case when exists (select * from user_favorites uf where uf.item_id = si.item_id and uf.user_id = si.user_id and is_deleted = 0) then 1 else 0 end as is_favorite
from
(select
sh.shop_id,
it.item_id,
us.user_id,
sh.shop_name,
it.item_name,
it.price
from shops sh inner join items it on sh.shop_id = it.shop_id
cross join users us
where sh.is_deleted = 0
and it.is_deleted = 0
and us.is_deleted = 0
order by shop_id, item_id, user_id) as si;

参考

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?