java - Hibernate ManyToOne 保存对象

我在保存具有 ManyToOne 双向关系的实体时遇到问题,这里是:

警告:HHH000437:正在尝试保存一个或多个与未保存的 transient 实体具有不可空关联的实体。在保存这些依赖实体之前,必须在操作中保存未保存的 transient 实体。 未保存的临时实体:([com.epam.apartmentsbooking.model.City#912]) 依赖实体:([[com.epam.apartmentsbooking.model.Apartment#640]]) 不可为 null 的关联:([com.epam.apartmentsbooking.model.Apartment.city])

org.hibernate.TransientPropertyValueException:非空属性引用 transient 值 - transient 实例必须在当前操作之前保存:com.epam.apartmentsbooking.model.Apartment.city -> com.epam.apartmentsbooking.model .城市

这看起来很常见,但我无法在数据库中保存引用现有城市的实体。

这是我的代码

Apartment.java:

package com.epam.apartmentsbooking.model;

import java.io.Serializable;

/**
 * <p>
 * Class describes the apartment entity
 */
public class Apartment implements Serializable {

private Long id;
private String name;
private String accommodationType;
private Byte numberOfGuests;
private Byte numberOfBedrooms;
private Byte numberOfBathrooms;
private Float pricePerDay;
private Float square;
private Short floor;
private String description;
private City city;


/**
 * Default constructor of the class
 * Should be used only when properties will be set later
 */
public Apartment() {
    id = -1L;
    name = "Default name";
    accommodationType = null;
    numberOfGuests = 0;
    numberOfBedrooms = 0;
    numberOfBathrooms = 0;
    pricePerDay = 0F;
    square = 0F;
    floor = 0;
    description = "Default description";
    city = new City();
}

/**
 * Counstructor that fully makes the object, should be used in most cases
 *
 * @param id                ID of the apartment
 * @param name              Name of the apartment
 * @param accommodationType Accommodation type (House, Apartment, Studio Apartment)
 * @param numberOfGuests    Maximum number of guests that host can place in the apartment
 * @param numberOfBedrooms  Number of bedrooms in the apartment
 * @param numberOfBathrooms Number of bathrooms in the apartment
 * @param pricePerDay       Price per day for the apartment
 * @param square            Square of the apartment (in sq. m.)
 * @param floor             Floor on which apartment is located
 * @param description       Description of the apartment
 * @param city              City where apartment is located
 */
public Apartment(Long id, String name, String accommodationType, Byte numberOfGuests, //NOSONAR
                 Byte numberOfBedrooms, Byte numberOfBathrooms, Float pricePerDay, Float square, Short floor,
                 String description, City city) {
    this.id = id;
    this.name = name;
    this.accommodationType = accommodationType;
    this.numberOfGuests = numberOfGuests;
    this.numberOfBedrooms = numberOfBedrooms;
    this.numberOfBathrooms = numberOfBathrooms;
    this.pricePerDay = pricePerDay;
    this.square = square;
    this.floor = floor;
    this.description = description;
    this.city = city;
}

/**
 * @return the id of the apartment
 */
public Long getId() {
    return id;
}

/**
 * @param id id of the apartment to be set
 */
public void setId(Long id) {
    this.id = id;
}

/**
 * @return the name of the apartment
 */
public String getName() {
    return name;
}

/**
 * @param name the name to be set
 */
public void setName(String name) {
    this.name = name;
}

/**
 * @return Accommodation type of the apartment
 */
public String getAccommodationType() {
    return accommodationType;
}

/**
 * @param accomodationType accommodationType to be set
 */
public void setAccommodationType(String accomodationType) {
    this.accommodationType = accomodationType;
}

/**
 * @return the maximum number of guest that may be placed in the apartment
 */
public Byte getNumberOfGuests() {
    return numberOfGuests;
}

/**
 * @param numberOfGuests number of guests to be set
 */
public void setNumberOfGuests(Byte numberOfGuests) {
    this.numberOfGuests = numberOfGuests;
}

/**
 * @return the number of bedrooms
 */
public Byte getNumberOfBedrooms() {
    return numberOfBedrooms;
}

/**
 * @param numberOfBedrooms the number of bedrooms to be set
 */
public void setNumberOfBedrooms(Byte numberOfBedrooms) {
    this.numberOfBedrooms = numberOfBedrooms;
}

/**
 * @return the number of bathrooms
 */
public Byte getNumberOfBathrooms() {
    return numberOfBathrooms;
}

/**
 * @param numberOfBathrooms the number of bathrooms to be set
 */
public void setNumberOfBathrooms(Byte numberOfBathrooms) {
    this.numberOfBathrooms = numberOfBathrooms;
}

/**
 * @return the price of stay per day
 */
public Float getPricePerDay() {
    return pricePerDay;
}

/**
 * @param pricePerDay the price to be set
 */
public void setPricePerDay(Float pricePerDay) {
    this.pricePerDay = pricePerDay;
}

/**
 * @return the square of the apartment in square meters
 */
public Float getSquare() {
    return square;
}

/**
 * @param square square of the apartment to be set
 */
public void setSquare(Float square) {
    this.square = square;
}

/**
 * @return floor number of the apartment
 */
public Short getFloor() {
    return floor;
}

/**
 * @param floor number of floor to be set
 */
public void setFloor(Short floor) {
    this.floor = floor;
}

/**
 * @return the description of the apartment
 */
public String getDescription() {
    return description;
}

/**
 * @param description description to be set
 */
public void setDescription(String description) {
    this.description = description;
}

/**
 * @return the city of the apartment
 */
public City getCity() {
    return city;
}

/**
 * @param city city to be set
 */
public void setCity(City city) {
    this.city = city;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Apartment apartment = (Apartment) o;

    if (!id.equals(apartment.id)) return false;
    if (!name.equals(apartment.name)) return false;
    if (accommodationType.equals(apartment.accommodationType)) return false;
    if (!numberOfGuests.equals(apartment.numberOfGuests)) return false;
    if (!numberOfBedrooms.equals(apartment.numberOfBedrooms)) return false;
    if (!numberOfBathrooms.equals(apartment.numberOfBathrooms)) return false;
    if (!pricePerDay.equals(apartment.pricePerDay)) return false;
    if (!square.equals(apartment.square)) return false;
    if (!floor.equals(apartment.floor)) return false;
    if (!description.equals(apartment.description)) return false;
    return city.equals(apartment.city);
}

@Override
public int hashCode() {
    int result = id.hashCode();
    result = 31 * result + name.hashCode();
    result = 31 * result + accommodationType.hashCode();
    result = 31 * result + numberOfGuests.hashCode();
    result = 31 * result + numberOfBedrooms.hashCode();
    result = 31 * result + numberOfBathrooms.hashCode();
    result = 31 * result + pricePerDay.hashCode();
    result = 31 * result + square.hashCode();
    result = 31 * result + floor.hashCode();
    result = 31 * result + description.hashCode();
    result = 31 * result + city.hashCode();
    return result;
}

@Override
public String toString() {
    return "Apartment{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", accomodationType=" + accommodationType +
            ", numberOfGuests=" + numberOfGuests +
            ", numberOfBedrooms=" + numberOfBedrooms +
            ", numberOfBathrooms=" + numberOfBathrooms +
            ", pricePerDay=" + pricePerDay +
            ", square=" + square +
            ", floor=" + floor +
            ", description='" + description + '\'' +
            ", city=" + city +
            '}';
}

城市.java:

package com.epam.apartmentsbooking.model;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

/**
 * <p>
 * Class describes city entity
 */
public class City implements Serializable {

private Long id;
private String name;
private Country country;
private Set<Apartment> apartments = new HashSet<>();

/**
 * Default constructor
 * Preferable not to use
 */
public City() {
    id = 912L;
    name = "Minsk";
    country = new Country();
}

/**
 * Creates new city object
 * Should be used in most cases
 *
 * @param id      Id of the city
 * @param name    Name of the city
 * @param country ISO Code of the country
 */
public City(Long id, String name, Country country) {
    this.id = id;
    this.name = name;
    this.country = country;
}

/**
 * @return the name of the city
 */
public String getName() {
    return name;
}

/**
 * @param name The name of the city to be set
 */
public void setName(String name) {
    this.name = name;
}

/**
 * @return Country object which the city is references on
 */
public Country getCountry() {
    return country;
}

/**
 * @param country Country object to set
 */
public void setCountry(Country country) {
    this.country = country;
}

/**
 * @return id of the city
 */
public Long getId() {
    return id;
}

/**
 * @param id id to be set
 */
public void setId(Long id) {
    this.id = id;
}

public Set<Apartment> getApartments() {
    return apartments;
}

public void setApartments(Set<Apartment> apartments) {
    this.apartments = apartments;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    City city = (City) o;

    if (id != null ? !id.equals(city.id) : city.id != null) return false;
    if (name != null ? !name.equals(city.name) : city.name != null) return false;
    if (country != null ? !country.equals(city.country) : city.country != null) return false;
    return apartments != null ? apartments.equals(city.apartments) : city.apartments == null;
}

@Override
public int hashCode() {
    int result = id != null ? id.hashCode() : 0;
    result = 31 * result + (name != null ? name.hashCode() : 0);
    result = 31 * result + (country != null ? country.hashCode() : 0);
    result = 31 * result + (apartments != null ? apartments.hashCode() : 0);
    return result;
}

@Override
public String toString() {
    return "City{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", country=" + country +
            ", apartments=" + apartments +
            '}';
}

ApartmentDaoImpl.java:

package com.epam.apartmentsbooking.dao.impl.hibernate;

import com.epam.apartmentsbooking.dao.ApartmentDao;
import com.epam.apartmentsbooking.model.Apartment;
import com.epam.apartmentsbooking.model.City;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;

/**
 *
 * Apartment DAO Implementation
 *
 * @see com.epam.apartmentsbooking.dao.ApartmentDao
 */
@Repository("apartmentDaoHibernate")
@Transactional
public class ApartmentDaoImpl extends GenericDaoImpl<Apartment, Long> implements ApartmentDao {

@Autowired
@Qualifier("sessionFactory")
private SessionFactory sessionFactory;

@Override
public Long create(Apartment newInstance) {

    return (Long) sessionFactory.getCurrentSession().save(newInstance);

}

@Override
public Apartment getByName(String name) {
    Query query = sessionFactory.getCurrentSession().createQuery("from Apartment where name=:name");
    query.setParameter("name", name);
    return (Apartment) query.uniqueResult();
}

@Override
public Collection<Apartment> getAll() {

    return sessionFactory.getCurrentSession().createCriteria(Apartment.class).list();
}

@Override
public Collection<Apartment> filter(String name, String accommodationType, Byte numberOfGuests,
                                    Byte numberOfBedrooms, Byte numberOfBathrooms, Float pricePerDay,
                                    Long cityId) {

    Criteria criteria = sessionFactory.getCurrentSession().createCriteria(Apartment.class);

    criteria.add(Restrictions.ilike("name", name + "%"));
    criteria.add(Restrictions.ilike("accommodationType", accommodationType + "%"));
    criteria.add(Restrictions.ge("numberOfGuests", numberOfGuests));
    criteria.add(Restrictions.ge("numberOfBedrooms", numberOfBedrooms));
    criteria.add(Restrictions.ge("numberOfBathrooms", numberOfBathrooms));
    criteria.add(Restrictions.le("pricePerDay", pricePerDay));
    criteria.add(Restrictions.eq("city.id", cityId));

    return criteria.list();
}

测试用例

    @Test
    @ExpectedDatabase("/apartments-test-create.xml")
    public void testCreate() {
        Apartment newApartment = new Apartment(null, "Test Apartment#4", "STUDIO", (byte) 3, (byte) 3, (byte) 3,
                400F, 78F, (short) 4, "EPAM Studio #2", new City());

        apartmentDao.create(newApartment);
    }

最佳答案

在您的 Apartment 类中,您必须告诉 hibernate 也可以将提交级联到“City”对象。您可以通过将 @Cascade 注释添加到 City 对象来执行此操作,如下所示:

@Cascade({ org.hibernate.annotations.CascadeType.ALL })
private City city;

现在,当您保留公寓时,也会同时保留城市。

或者,首先保留您的“城市”对象,然后使用保留的城市对象构建公寓对象。

https://stackoverflow.com/questions/45486361/

相关文章:

c# - 更改 RedirectUri、Nginx 和 ASP.NEt Core

spring - @Document(collection = "Test") 在 MongoRep

javascript - 如何将样式(css)和js添加到 Handlebars 文件?

ios - Xcode 和安装的 SF Pro Display(Apple 字体)

selenium - 使用 SendKeys 向 IE 11 发送 '@' 特殊字符

vue.js - token 失效时自动注销用户

python-3.x - Python 3 文件共享变量

php - 使用 PHP curl 从 Google 的地理编码 API 返回数据

outlook - 仅在 Web 上启用 Outlook Web 插件

hibernate - 如何将表名作为参数传递给 HQL 查询