【问题标题】:How to update foreign key in one table with the primary key of the table that it references using JPA?如何使用 JPA 引用的表的主键更新一个表中的外键?
【发布时间】:2013-05-16 11:05:35
【问题描述】:

我有两个表如下,

用户

+--------+---------------+------------+--------+-----------+------------+--------------+----------------+----------------+-------------+
| USERID | EMAIL         | FIRST_NAME | HONORS | LAST_NAME | LOGIN_TYPE | PHONE_NUMBER | PROFILE_PIC    | RECENT_CONV_ID | LOCATION_ID |
+--------+---------------+------------+--------+-----------+------------+--------------+----------------+----------------+-------------+
|      1 | asf@gmail.com | ghj        |      0 | ert       |          0 | 9879878      | http://vvv.com |           NULL |        NULL |

+--------+---------------+------------+-------- +-----------+------------+--------------+--------- -------+----------------+-------------+

USER_LOCATION

+------------+-------+---------+----------+------------+-----------+-------+
| LOCATIONID | CITY  | COUNTRY | LATITUDE | LOCAL_ADDR | LONGITUDE | STATE |
+------------+-------+---------+----------+------------+-----------+-------+
|          1 | xyz   | mm      |       10 | asfdasf    |        10 | qqq   |
+------------+-------+---------+----------+------------+-----------+-------+

下面是两个表的 CREATE TABLE 查询,

CREATE TABLE `USER` (
`USERID` bigint(20) NOT NULL AUTO_INCREMENT,
`EMAIL` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
`FIRST_NAME` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
`HONORS` bigint(20) NOT NULL,
`LAST_NAME` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
`LOGIN_TYPE` int(11) NOT NULL,
`PHONE_NUMBER` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
`PROFILE_PIC` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
`RECENT_CONV_ID` bigint(20) DEFAULT NULL,
`LOCATION_ID` bigint(20) DEFAULT NULL,
 PRIMARY KEY (`USERID`),
 KEY `USER_N50` (`RECENT_CONV_ID`),
 KEY `USER_N49` (`LOCATION_ID`),
 CONSTRAINT `USER_FK1` FOREIGN KEY (`RECENT_CONV_ID`) REFERENCES `RECENT_CONVERSATION` (`ID`),
 CONSTRAINT `USER_FK2` FOREIGN KEY (`LOCATION_ID`) REFERENCES `USER_LOCATION` (`LOCATIONID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1 |


CREATE TABLE `USER_LOCATION` (
`LOCATIONID` bigint(20) NOT NULL AUTO_INCREMENT,
`CITY` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`COUNTRY` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`LATITUDE` double DEFAULT NULL,
`LOCAL_ADDR` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`LONGITUDE` double DEFAULT NULL,
`STATE` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
 PRIMARY KEY (`LOCATIONID`),
 UNIQUE KEY `USER_LOCATION_U1` (`LOCAL_ADDR`,`CITY`,`STATE`,`COUNTRY`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1

现在,我想用 USER_LOCATION 的 LOCATIONID 更新 USER 中的 LOCATION_ID。我如何使用 JPA 来实现它?

我的 Java 类:

@Entity(name="USER")
public class User {

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private long userId;

@Column(name="PHONE_NUMBER", nullable=false)
private String phoneNumber;

@Column(name="FIRST_NAME", nullable=false)
private String firstName;

@Column(name="LAST_NAME", nullable=false)
private String lastName;

@Column(name="EMAIL", nullable=false)
private String email;

@Column(name="PROFILE_PIC", nullable=false)
private String profilepic;

@Column(name="LOGIN_TYPE", nullable=false)
private int loginType;

@Column(name="HONORS", nullable=false)
private long honors;

@ManyToOne(cascade={CascadeType.PERSIST})
@JoinColumn(name="LOCATION_ID")
private UserLocation userLocation;

@OneToMany(cascade=CascadeType.PERSIST)
@JoinColumn(name="RECENT_CONV_ID")
private RecentConversation recentConversation;
 }

@Entity(name="USER_LOCATION")
@Table(name="USER_LOCATION", uniqueConstraints=@UniqueConstraint(columnNames={"LOCAL_ADDR", "CITY", "STATE", "COUNTRY"}))
@NamedQuery(name="addUserLocation", query="SELECT l FROM USER_LOCATION l " +
                                        "WHERE l.local_addr = :lo_addr AND " +
                                        "l.city = :city AND " +
                                        "l.state = :state AND " +
                                        "l.country = :country")
public class UserLocation {
@Id
@GeneratedValue
private long locationId;

@Column(name="LATITUDE")
private Double latitude;

@Column(name="LONGITUDE")
private Double longitude;

@Column(name="LOCAL_ADDR")
private String local_addr;

@Column(name="CITY")
private String city;

@Column(name="STATE")
private String state;

@Column(name="COUNTRY")
private String country;

@OneToMany(mappedBy="userLocation")
private Collection<User> users = new HashSet<User>();
}

请注意,我尝试实施的业务规则是,在 USER_LOCATION 中不应有重复条目,基于 UNIQUE KEY USER_LOCATION_U1。此外,如果同一位置有多个用户,则 USER 中的 LOCATION_ID 应更新为该 USER_LOCATION。非常感谢。

更新: 我的测试用例,

public class UserTest extends TestCase{

EntityManager em;

public void testUsersFromLocation() {

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("TalkExchange");
    em = emf.createEntityManager();

    User user = createNewUser();

    em.getTransaction().begin();
//      em.persist(user.getUserLocation());
    em.merge(user);
    em.flush();
    em.detach(user.getUserLocation());
    em.contains(user.getUserLocation());
    em.contains(user);
    em.getTransaction().commit();

    getUsersAtLocation();
}

    public User createNewUser() {
    User user = new User();
    user.setEmail("asf@gmail.com");
    user.setFirstName("fgfg");
    user.setLastName("uiu");
    user.setLoginType(0);
    user.setPhoneNumber("7777");
    user.setProfilepic("http://vvv.com");
    user.setUserId(234);
    UserLocation userLocation = createUserLocation();
    user.setUserLocation(userLocation);
//      UserLocation userLocation = getExistingUserLocation(); 
//      user.setUserLocation(userLocation);
    userLocation.getUsers().add(user);
    return user;
}

    public User createNewUser() {
    User user = new User();
    user.setEmail("asf@gmail.com");
    user.setFirstName("fgfg");
    user.setLastName("uiu");
    user.setLoginType(0);
    user.setPhoneNumber("7777");
    user.setProfilepic("http://vvv.com");
    user.setUserId(234);
    UserLocation userLocation = createUserLocation();
    user.setUserLocation(userLocation);
//      UserLocation userLocation = getExistingUserLocation(); 
//      user.setUserLocation(userLocation);
    userLocation.getUsers().add(user);
    return user;
}

    public UserLocation createUserLocation() {
    UserLocation userLocation = new UserLocation();
    userLocation.setCity("wrwer");
    userLocation.setCountry("MM");
    userLocation.setLatitude(new Double(10));
    userLocation.setLongitude(new Double(10));
    userLocation.setLocal_addr("dfdfd");
    userLocation.setState("kjlkj");

//      create a query to find out whether the above UserLocation exists in the database.
//      if(exists)
//          use the existing location
//      else
//          use add the new location
    addLocationRule(userLocation);
    return userLocation;
}
}

【问题讨论】:

    标签: mysql jpa datanucleus


    【解决方案1】:

    您应该从此关系中删除 CascadeType.PERSIST

    @ManyToOne(cascade={CascadeType.PERSIST})
    @JoinColumn(name="LOCATION_ID")
    private UserLocation userLocation;
    

    Cascade persist 意味着每当您保存一个新的 User 实例时,它都会尝试保存一个新的 UserLocation。这显然不是您在多对一关系中想要的。

    您应该在创建 User 对象之前创建 UserLocation 实例,然后在用户实例中重复使用相同的用户位置。

    【讨论】:

    • 如果相关的 UserLocation 在 User 被持久化时被分离,那么它不会创建任何新的 UserLocation 对象(即使设置了 CascadeType.PERSIST)并且只引用现有的
    • 感谢 Doron,DataNucleus。多伦,我明白你的意思。评论了它。由于 UserLocation 上的 @UniqueConstraint,事务仍然回滚。现在,如果我错了,请纠正我,当我持久化用户对象时,它也会尝试持久化 userLocation 对象。那么,CascadeType.PERSIST 是否默认在 persist() 方法上启用?
    • DataNucleus,如何将新的 User 对象“引用”到现有的 userLocation 对象?请多多包涵。
    • 您在 txn 中“找到”该对象,并在持久化用户之前将您的用户类中的引用设置为它。没有什么特定于 Java 持久性的,它的 Java ...“引用”等同于使用对的引用
    • 感谢 DataNucleus 一直都在。我意识到我的概念并不清楚。我已经发布了详细的答案。
    【解决方案2】:

    在这种情况下,

    1. 使用entityManager.find()命名查询查找 UserLocation 对象。
    2. 由于 User 和 UserLocation 有双向关系,我想将新用户添加到现有位置userLocation.getUsers.add(newUser);

    就是这样。它将向 USER 表添加一个新用户,该用户带有一个外键,指向 USER_LOCATION 表中现有的 USER_LOCATION 行。

    具体实现请查看gist

    更新:请多次运行测试,以便将更多用户添加到现有位置。

    【讨论】:

      猜你喜欢
      • 2018-08-15
      • 2018-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多