Java模型休眠映射问题

问题描述

注意:我使用Spring Data Jpa来保持持久性。

问题:

我有两种型号:用户徽章

我有一个用户拥有的徽章列表,作为 User 类中的数据成员。 我也有用户作为徽章类(即徽章的创建者)中的数据成员

我想在用户和徽章列表数据成员之间建立关系。 关系的类型为OnetoMany(即,一个用户将拥有许多徽章),反之亦然。

我希望它以这种方式工作, 在代码中,

当我将发布者(又名“用户”)设置为特定用户对象的情况下保存徽章对象时,则无需将其(徽章)添加到该用户拥有的徽章列表中。

我尝试创建关系,但是它在REST API响应中返回了用户拥有的徽章的空列表。

徽章模型

import javax.persistence.*;

@Entity
@Table(name = "badges")
public class Badge {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "badge_id")
    private int mId;
    @Column(name = "badge_name",nullable = false,unique = true)
    private String mName;
    @Column(name = "badge_description")
    private String mDescription;
    @Lob
    @Column(name = "badge_logo",nullable = false)
    private String mlogo;
    @ManyToOne
    @JoinColumn(name = "issuer_id")
    private User mIssuer;
}

用户模型

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "users")
public class User {
    @Id@GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "user_id")
    private long mId;
    @Column(name = "username",unique = true)
    private String mUserName;
    @Column(name = "fullname",nullable = false)
    private String mFullName;
    @Column(name = "salt")
    private String mSalt;
    @OnetoMany(mappedBy = "mIssuer",cascade = CascadeType.ALL)
    private List<Badge> mOwnedBadges;
    @OnetoMany
    @JoinColumn(name = "received_badges_id")
    private List<Badge> mReceivedBadges;
}

CommandLineRunner

import com.badging.spinnerbadger.SpinnerBadger.Models.Badge;
import com.badging.spinnerbadger.SpinnerBadger.Models.User;
import com.badging.spinnerbadger.SpinnerBadger.Services.Intefaces.BadgeSerivce;
import com.badging.spinnerbadger.SpinnerBadger.Services.Intefaces.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
public class StartupExecutor implements CommandLineRunner {
    @Autowired
    private BadgeSerivce mBadgeSerivce;
    @Autowired
    private UserService mUserService;

    @Override
    public void run(String... args) throws Exception {
        //Todo:: issuer cannot issue badge to itself
        final User user1 = new User();
        user1.setFullName("User1 FullName");
        user1.setSalt("salt1");
        user1.setUserName("User1 UserName");
        mUserService.save(user1);

        final User user2 = new User();
        user2.setFullName("User2 FullName");
        user2.setSalt("salt2");
        user2.setUserName("User2 UserName");
        mUserService.save(user2);


        Badge badge1 = new Badge();
        badge1.setDescription("Desc1");
        badge1.setlogo("logo1");
        badge1.setName("Badge1");
        badge1.setIssuer(user1);
        mBadgeSerivce.save(badge1);

        Badge badge2 = new Badge();
        badge2.setDescription("Desc2");
        badge2.setlogo("logo2");
        badge2.setName("Badge2");
        badge2.setIssuer(user2);
        mBadgeSerivce.save(badge2);

        Badge badge3 = new Badge();
        badge3.setDescription("Desc3");
        badge3.setlogo("logo3");
        badge3.setName("Badge3");
        badge3.setIssuer(user1);
        mBadgeSerivce.save(badge3);


        user1.setReceivedBadges(Arrays.asList(badge2));

        user2.setReceivedBadges(Arrays.asList(badge1,badge3));
    }
}

注意:它也不会保存用户的“已接收徽章”列表,如果您也能确定这一点,那么我将非常感谢您。

BadgeRepo

import com.badging.spinnerbadger.SpinnerBadger.Models.Badge;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface BadgeRepo extends PagingAndSortingRepository<Badge,Long> {
}

UserRepo

import com.badging.spinnerbadger.SpinnerBadger.Models.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepo extends JpaRepository<User,Long> {
}

BadgeServiceImpl

package com.badging.spinnerbadger.SpinnerBadger.Services.Implentations;

import com.badging.spinnerbadger.SpinnerBadger.Repository.BadgeRepo;
import com.badging.spinnerbadger.SpinnerBadger.Models.Badge;
import com.badging.spinnerbadger.SpinnerBadger.Services.Intefaces.BadgeSerivce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class BadgeServiceImpl implements BadgeSerivce {
    @Autowired
    private BadgeRepo mBadgeRepo;
    @Override
    public List<Badge> getAllBadges(int pageNumber,int sizeOfPage) {
        if (sizeOfPage > 20) {
            sizeOfPage = 20;
        }
        final Page<Badge> allPages = mBadgeRepo.findAll(PageRequest.of(pageNumber,sizeOfPage));
        if (allPages.getTotalElements() > 0) {
            return allPages.toList();
        } else{
            return new ArrayList<Badge>();
        }
    }

    @Override
    public void save(Badge badge) {
        mBadgeRepo.save(badge);
    }
}

UserServiceImpl

import com.badging.spinnerbadger.SpinnerBadger.Models.Badge;
import com.badging.spinnerbadger.SpinnerBadger.Models.User;
import com.badging.spinnerbadger.SpinnerBadger.Repository.UserRepo;
import com.badging.spinnerbadger.SpinnerBadger.Services.Intefaces.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserRepo mUserRepo;
    @Override
    public void save(User user) {
        mUserRepo.save(user);
    }

    @Override
    public List<Badge> getUsersReceivedBadgeList(long userId) {
        final Optional<User> byId = mUserRepo.findById(userId);
        return byId.orElse(new User()).getReceivedBadges();
    }

    @Override
    public List<Badge> getUserOwnedBadgeList(long userId) {
        final Optional<User> byId = mUserRepo.findById(userId);
        return byId.orElse(new User()).getReceivedBadges();
    }
}

由Hibernate生成sql ->用户模型第一,徽章模型第二

Hibernate: insert into users (fullname,salt,username,user_id) values (?,?,?)

Hibernate: insert into badges (badge_description,issuer_id,badge_logo,badge_name,badge_id) values (?,?)

解决方法

我在这里看到了几处可能出错的地方。

  1. 您似乎没有指定交易。将@Transactional添加到应参与事务的bean或方法中。至少应包括(最终)修改数据库的所有内容,即修改托管实体的任何语句,包括从数据库和save语句中加载该语句的语句。我希望这是您遇到的问题的真正原因。

  2. 您似乎没有适当的代码来同步双向关系的双方。所以,当你打电话 badge1.setIssuer(user1)user1不会更新,因此如果您致电user1.getOwnedBadges() 仍然会返回不变的(空)值。

    在这种情况下,我怀疑这是一个问题,但这会导致 一次交易中看起来不同的关系, 取决于您从哪一侧看。并更改为 非拥有方(在您的情况下为User)将不会持续存在。所以应该解决 无论如何。另请参见https://vladmihalcea.com/jpa-hibernate-synchronize-bidirectional-entity-associations/

  3. 保存实体时,应使用save方法返回的实例,而不要使用传递给save作为参数的实例。通常它们是相同的,但是当它们不修改传递给save的那个时,可能不会导致状态持久化到数据库。

如果这些问题已解决且问题仍然存在,我建议以下内容收集有关发生的情况的更多信息:

  1. 激活logging of SQL statements including parameters以便查看实际保留的内容(以及时间)。

  2. 创建一个JUnit测试来测试您的服务。这样可以更清楚地了解实际执行的操作,并允许创建要比较的变体。