Spring Data JPA @ManyToMany に苦しんだ

未分類
スポンサーリンク

Spring Data JPAの @ManyToMany にこの2日間苦しめられた。

使用しているバージョンは、Springboot v2.5

ネットで@ManyToManyを検索して、紹介されていたコードを片っ端から試してみたが、参考にしたコードはどれもUserが所有するRoleを取得することが出来なかった。

User を取得したところで実行を止めて変数の値をみると、以下のエラーが表示されていた。

エラー 例外が発生しました: メソッドを起動中に com.sun.jdi.InvocationException: Exception occurred in target VM が発生しました。

このエラーメッセージに2日間苦しめられていたが、ようやく、正常に動くコードを今朝、見つけることが出来たので、記録として残しておく。

User.java

package com.example.demo.entity;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@Entity
@Table
public class User implements Serializable {
	public User(String userName, String password) {
		this.userName = userName;
		this.password = password;
	}
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "user_id")
	private Long id;

    @Column(name = "enabled")
    private boolean enabled = true;

	@Column(name = "username")
	private String userName;

	@Column(name = "password")
	private String password;

    @Column(name = "dept")
    private String dept;

    @ManyToMany(fetch=FetchType.EAGER)
    @JoinTable(name="user_role",
    		joinColumns = @JoinColumn(name="user_id", referencedColumnName="user_id"),
 		inverseJoinColumns = @JoinColumn(name="role_id", referencedColumnName="role_id"))
    private List<Role> roles = new ArrayList();
}

Role.java

package com.example.demo.entity;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@Entity
@Table
public class Role implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "role_id")
    private Long id;
    private String name;

    public Role(String name) {
        this.name = name;
    }
}

これで無事、rolesを取得することが出来ました。

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
      : 
    User user = userRepository.findByName(username);    
    List<Role> roles = user.getRoles(); 
      :

理由は判りませんが、fetch=FetchType.EAGER の指定が必須でした。

ネットで紹介されていたコードには、FetchType.EAGER を指定している例がひとつも無かったので、どこかのタイミングで仕様が変更にでもなったのだろうか?

@ManyToManyは、デフォルトで FetchType.LAZY になるらしいが、参照先のデータ量が大きい場合、このLAZYローディングが機能しないと使い物にならないように思うが、今日のところは、これで良しとしよう。


コメント