Spring数据JPA延迟加载

huangapple go评论127阅读模式
英文:

Spring data JPA lazy loading

问题

我很新于Java Spring,已经试了几天来使我的关系工作,但是还没有成功。我正在使用Java Spring和Mysql。

管理员应该将员工添加到数据库中。员工只能看到特定的数据。我的问题是,我不知道如何从EmployeesController正确地进行POST请求,并从User模型中获取user_id。我尝试了一些不同的实现,但仍然无法使其工作。

我有一个用于Users、Roles和Employees的实体。员工始终是用户,但用户可能不是员工。所以我的数据库结构如下:

users:

+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int          | NO   | PRI | NULL    | auto_increment |
| username | varchar(30)  | NO   |     | NULL    |                |
| email    | varchar(100) | NO   |     | NULL    |                |
| password | varchar(100) | NO   |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+

employees:

+-------------------+--------------+------+-----+---------+----------------+
| Field             | Type         | Null | Key | Default | Extra          |
+-------------------+--------------+------+-----+---------+----------------+
| id                | int          | NO   | PRI | NULL    | auto_increment |
| academic_title    | varchar(100) | NO   |     | NULL    |                |
| department        | varchar(100) | NO   |     | NULL    |                |
| user_id           | int          | NO   |     | NULL    |                |
+-------------------+--------------+------+-----+---------+----------------+

只有管理员可以将员工添加到系统中,员工只能登录并查看一些数据。就像我了解的那样,在Java Spring中,像employees表中的user_id这样的参数不会额外写入模型中。所以这是我现在有的:

Employee.java

@Entity
@Table(name = "employees")
public class Employee{
  
    @OneToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Size(min=3, max = 100)
    private String academic_title;

    @Size(min=3, max = 100)
    private String department;

    public Employee() {}
 
    public Employee(String academic_title, String department) {
        super();
        this.academic_title = academic_title;
        this.department = department;
    }
    
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return academic_title;
    }
 
    public void setTitle(String academic_title) {
        this.academic_title = academic_title;
    }

    public String getDepartment() {
        return department;
    }
 
    public void setDepartment(String department) {
        this.department = department;
    }

    public User getUser() {
		return user;
	}

	public void setUser(User user) {
		this.user = user;
	}
}

User.java

@Entity
@Table(name = "users")
public class User{
  @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
  
    @NotBlank
    @Size(min=3, max = 50)
    private String username;
 
    @NaturalId
    @NotBlank
    @Size(max = 50)
    @Email
    private String email;
 
    @NotBlank
    @Size(min=6, max = 100)
    private String password;
 
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "user_roles", 
      joinColumns = @JoinColumn(name = "user_id"), 
      inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles = new HashSet<>();

    @OneToOne(fetch = FetchType.LAZY,
            cascade =  CascadeType.ALL,
            mappedBy = "user")
    private Employee employee;
 
    public User() {}
 
    public User(String username, String email, String password) {
        super();
        this.username = username;
        this.email = email;
        this.password = password;
    }
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
 
    public String getPassword() {
        return password;
    }
 
    public void setPassword(String password) {
        this.password = password;
    }
 
    public Set<Role> getRoles() {
        return roles;
    }
 
    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

    public Employee getEmployee() {
		return employee;
	}

	public void setEmployee(Employee employee) {
		this.employee = employee;
	}
}

EmployeeController.java (创建Employee函数)

public Employee createEmployee(@PathVariable (value = "user_id") Long user_id,
                                 @Valid @RequestBody Employee employee) {
                                     
        userRepository.findById(user_id);
        employeeRepository.save(employee);
        return employee;
}

UserRepository.java

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
    Optional<User> findById(Long id);
    Boolean existsByUsername(String username);
    Boolean existsByEmail(String email);
}

我的Postman POST请求来添加员工:

{
    "academic_title": "xy",
    "department": "xx",
    "user_id": 5
}

当我尝试将user_id保存为普通的Long参数时,我可以将其保存到数据库。但是当我获取现有的员工时,连接功能不起作用,所以我无法查看user_id为5的用户是谁。
当我尝试另一种方式,就像上面的代码一样时,我得到了一个错误,类似于“非空属性引用了一个空或临时值”,或者类似于“Employee列:user_id(应映射为insert="false" update="false")”。所以我不知道该怎么办,我在这个问题上卡了5天。

提前感谢您的帮助!

英文:

I'm very new to Java Spring and I'm trying for a few days to get my relationship working, but it doesn't. I'm using Java Spring and Mysql.

The Admin should add Employees to the database. The Employees will only see specific data. My problem is, that I don't understand how I can make a right POST request from the EmployeesController and get the user_id from the User Model. I tried some different implementations, but I still cannot get it to work.

I have an entity for Users, Roles and Employees. An employee is always a user, but a user may not be an employee. So my database structure is as follows:

users:

+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int          | NO   | PRI | NULL    | auto_increment |
| username | varchar(30)  | NO   |     | NULL    |                |
| email    | varchar(100) | NO   |     | NULL    |                |
| password | varchar(100) | NO   |     | NULL    |              
+----------+--------------+------+-----+---------+----------------+

employees:

+-------------------+--------------+------+-----+---------+----------------+
| Field             | Type         | Null | Key | Default | Extra          |
+-------------------+--------------+------+-----+---------+----------------+
| id                | int          | NO   | PRI | NULL    | auto_increment |
| academic_title    | varchar(100) | NO   |     | NULL    |                |
| department        | varchar(100) | NO   |     | NULL    |                |
| user_id           | int          | NO   |     | NULL    |                |
+-------------------+--------------+------+-----+---------+----------------+

Only an admin can add an employee to the system, the employees can only log in and see some data. So like I understand in Java Spring parameters like user_id in employees table are not extra written in the model. So this is what I have right now:

Employee.java

@Entity
@Table(name = &quot;employees&quot;)
public class Employee{
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = &quot;user_id&quot;, nullable = false)
private User user;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Size(min=3, max = 100)
private String academic_title;
@Size(min=3, max = 100)
private String department;
public Employee() {}
public Employee(String academic_title, String department) {
super();
this.academic_title = academic_title;
this.department = department;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}

User.java

@Entity
@Table(name = &quot;users&quot;)
public class User{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank
@Size(min=3, max = 50)
private String username;
@NaturalId
@NotBlank
@Size(max = 50)
@Email
private String email;
@NotBlank
@Size(min=6, max = 100)
private String password;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = &quot;user_roles&quot;, 
joinColumns = @JoinColumn(name = &quot;user_id&quot;), 
inverseJoinColumns = @JoinColumn(name = &quot;role_id&quot;))
private Set&lt;Role&gt; roles = new HashSet&lt;&gt;();
@OneToOne(fetch = FetchType.LAZY,
cascade =  CascadeType.ALL,
mappedBy = &quot;user&quot;)
private Employee employee;
public User() {}
public User(String username, String email, String password) {
super();
this.username = username;
this.email = email;
this.password = password;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set&lt;Role&gt; getRoles() {
return roles;
}
public void setRoles(Set&lt;Role&gt; roles) {
this.roles = roles;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
}

EmployeeController.java (create Employee function)

    public Employee createEmployee(@PathVariable (value = &quot;user_id&quot;) Long user_id,
@Valid @RequestBody Employee employee) {
userRepository.findById(user_id);
employeeRepository.save(employee);
return employee;
}

userRepository.java

@Repository
public interface UserRepository extends JpaRepository&lt;User, Long&gt; {
Optional&lt;User&gt; findByUsername(String username);
Optional&lt;User&gt; findById(Long id);
Boolean existsByUsername(String username);
Boolean existsByEmail(String email);
}

My Postman POST request to add employees:

{
&quot;academic_title&quot;: &quot;xy&quot;,
&quot;department&quot;: &quot;xx&quot;,
&quot;user_id&quot;: 5
}

When I try to save the user_id as a normal Long parameter, I can save it to the database. But then when I fetch the existing employees, the join function doesn't work so I cannot see who user_id 5 is.
And when I try it the other way, like the code above I'm getting an error like: not-null property references a null or transient value
or something like
Employee column: user_id (should be mapped with insert="false" update="false"). So I don't know what to do, I'm stuck in this problem for 5 days.

Thanks in advance for your help!

答案1

得分: 2

如果您决定使用Hibernate和注解@OneToOne来定义您的关系,现在您需要记住Employee类拥有一个名为User的类属性(属性)。为了在用户表上存储来自员工表的外键,您需要将User实例设置为Employee。

在您的情况下,您可以尝试类似以下的代码:

public Employee createEmployee(@PathVariable(value = "user_id") Long user_id,
                               @Valid @RequestBody Employee employee) {
                                   
    // 通过ID查找用户,否则抛出异常
    User userById = userRepository.findById(user_id).orElseThrow(() -> new 
    RuntimeException("Not found."));
    
    // 将用户设置到员工中,这类似于绑定两个实体
    employee.setUser(userById);

    // 然后您可以保存员工实体。
    return employeeRepository.save(employee);
}
英文:

If you decided to define your relationship using Hibernate and annotation @OneToOne, you should keep in your mind now Employee has a class property (attribute) User. And for store foreign key from the employee table on the user table you need to set User instance to Employee.

You should try something like this in your case:

public Employee createEmployee(@PathVariable (value = &quot;user_id&quot;) Long user_id,
@Valid @RequestBody Employee employee) {
// find user by id or else throw exception
User userById = userRepository.findById(user_id).orElseThrow(() -&gt; new 
RuntimeException(&quot;Not found.&quot;));
// set user into employee. it&#39;s like binding two entities
employee.setUser(userById);
// after that you can save your employee entity.
return employeeRepository.save(employee);
}

答案2

得分: 0

join函数不起作用

User和Employee之间的获取类型是懒加载。这意味着在加载员工时实际上不会加载用户。相反,它会在被请求时加载用户。

所以正如@Zooger提到的,你应该首先从数据库加载用户。

英文:

> the join function doesn't work

the fetch type between User and Employee is Lazy. which means that it does not actually load the user when loading an employee. Instead, it loads them when requested to do so.

So as @Zooger mentioned, you should load the user from database first.

huangapple
  • 本文由 发表于 2020年9月14日 20:12:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/63884091.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定