休眠时冗余数据多对一映射
Redundant data while hibernate mapping many to one
我有 2 个 tables:旅行和地点(多对一),我的问题是当我添加 2 个数据不同但地点相同的旅行时,它会向 Trip 添加 2 条记录table 和 2 条记录到 Place table,而它应该只添加 1 条记录到 Place。
例如,我有 2 次不同日期的旅行,但它们都在同一个地方 - 意大利、罗马。所以这些数据应该只有一个记录:意大利,罗马。
如何在我的应用程序中避免此类行为?
行程class:
public class Trip implements java.io.Serializable {
private int idTrip;
private int idHotel;
private Date date;
private int cost;
private int profit;
private String organisator;
private int period;
private String food;
private String transport;
private int persons;
private int kidsAmount;
private String ownerName;
private String ownerLastName;
private Place place;
+ 构造函数、get() 和 set() 方法,
地点class:
public class Place implements java.io.Serializable {
private int idPlace;
private String country;
private String city;
private String island;
private String information;
private Set<Trip> trips;
+ 构造函数、get() 和 set() 方法,
行程映射文件:
<hibernate-mapping>
<class name="pl.th.java.biuro.hibernate.Trip" table="Trip">
<id column="idTrip" name="idTrip" type="int">
<generator class="native"/>
</id>
<property column="date" name="date" type="date"/>
<property column="cost" name="cost" type="int"/>
<property column="profit" name="profit" type="int"/>
<property column="organisator" name="organisator" type="string"/>
<property column="period" name="period" type="int"/>
<property column="food" name="food" type="string"/>
<property column="transport" name="transport" type="string"/>
<property column="persons" name="persons" type="int"/>
<property column="kidsAmount" name="kidsAmount" type="int"/>
<property column="idHotel" name="idHotel" type="int"/>
<many-to-one fetch="select" name="place" class="pl.th.java.biuro.hibernate.Place">
<column name="idPlace" not-null="true"></column>
</many-to-one>
</class>
</hibernate-mapping>
放置映射文件:
<hibernate-mapping>
<class name="pl.th.java.biuro.hibernate.Place" table="Place">
<id column="idPlace" name="idPlace" type="int">
<generator class="native"/>
</id>
<property column="country" name="country" type="string"/>
<property column="city" name="city" type="string"/>
<property column="island" name="island" type="string"/>
<property column="information" name="information" type="string"/>
<set name="trips" table="Trip" inverse="true" lazy="true" fetch="select">
<key>
<column name="idPlace" not-null="true" />
</key>
<one-to-many class="pl.th.java.biuro.hibernate.Trip" />
</set>
</class>
</hibernate-mapping>
我还用我的 MySQL 数据库添加了一些屏幕截图,也许有一些问题不允许我正确地执行此操作:
MySQL database
编辑: 在 Place 映射文件和 Set in Place 中添加一对多关系 class,但仍然遇到同样的问题。
EDIT2: 将具有持久实体的代码添加到数据库中:
Session session = DatabaseConnection.getFactory().openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
trip.setPlace(place);
session.save(place);
session.save(trip);
tx.commit();
} catch (HibernateException e) {
if (tx != null) {
tx.rollback();
}
System.out.println("Exception found while adding new trip: " + e.getMessage());
e.printStackTrace();
} finally {
session.close();
}
但我仍然遇到同样的问题...我添加了一个地点 A 的行程,然后在下一步中我添加了另一个相同的行程,这是我得到的:
EDIT3: 创建 Trip 和 Place 对象:
Trip trip = null;
Place place = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateInString = tripDateField.getText();
java.util.Date date = null;
try {
date = sdf.parse(dateInString);
place = new Place(tripCountryField.getText(), tripCityField.getText(), tripIslandField.getText(), tripPlaceInfoTextArea.getText());
int period = 0, persons = 0, kidsAmount = 0;
//W razie braku niewymaganych liczbowych danych ustawiane są wartości -1
if (tripPeriodField.getText().equals("")) {
period = -1;
}
if (tripPersonsField.getText().equals("")) {
persons = -1;
}
if (tripKidsAmountField.getText().equals("")) {
kidsAmount = -1;
}
trip = new Trip(new Date(date.getTime()), Integer.parseInt(tripCostField.getText()), Integer.parseInt(tripProfitField.getText()),
tripOrganisatorField.getText(), period, tripFoodField.getText(), tripTransportField.getText(),
persons, kidsAmount, tripClientNameField.getText(), tripClientLastNameField.getText());
} catch (ParseException ex) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
try {
date = sdf.parse("0000-00-00");
} catch (ParseException ex1) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex1);
}
} catch (NumberFormatException e) {
place = new Place("111", "111", "111", "111");
trip = new Trip(null, WIDTH, WIDTH, dateInString, WIDTH, dateInString, dateInString, WIDTH, ABORT, dateInString, dateInString);
System.out.println("Exception while getting trip / place data: " + e.toString());
}
dataEdition.addTrip(trip, place, dataEdition.validateTripData(trip, place), addRemoveSearchTripDialog);
我从 textFields 获取这些对象数据并在需要时将它们解析为 int,所以我想这应该没问题。在此之后,我将这两个对象传递到另一个方法中,我将它们保存到数据库中。
我试过这个,没有重复:
并在数据库中创建两个 classes 和 tables,我在与 classes 相同的包中创建了两个映射文件。
地点映射:
<hibernate-mapping package="pl.th.java.biuro.hibernate">
<class name="Place" table="PLACE">
<id name="idPlace" type="java.lang.Long" column="ID_PLACE">
<generator class="native">
</generator></id>
<set name="trips" table="Trip" inverse="true" lazy="true" fetch="select">
<key>
<column name="idPlace" not-null="true">
</column></key>
<one-to-many class="pl.th.java.biuro.hibernate.Trip">
</one-to-many></set>
</class>
</hibernate-mapping>
以及行程映射:
<hibernate-mapping package="pl.th.java.biuro.hibernate">
<class name="Trip" table="TRIP">
<id name="idTrip" type="java.lang.Long" column="ID_TRIP">
<generator class="native">
</generator></id>
<many-to-one name="place" class="pl.th.java.biuro.hibernate.Place" fetch="select">
<column name="idPlace" not-null="true">
</column></many-to-one>
</class>
</hibernate-mapping>
我看到,您的行程 ID 不是在您的 table 上生成的?那是对的吗?而且您的命名约定有点容易出错。尝试将 db tables 命名为大写并将 class 中的 set 变量命名为 maybe trips。在您的映射中,您将 id 映射到一个 int。大多数数据库将其映射为长,所以可能也有错误。
您似乎在每次提交时都创建了一个 NEW 地点。
Place place = new Place(tripCountryField.getText(), tripCityField.getText(), tripIslandField.getText(), tripPlaceInfoTextArea.getText());
然后您希望 Hibernate 以某种方式神奇地 确定您实际上可能想要使用数据库中的现有条目。
这行不通。
如果提交的地点已经存在,那么您需要以某种方式加载现有的持久实体并使用它。
您可以通过允许用户 select 现有地点并通过该地点的 ID 发送来在前端解决此问题,或者查询匹配的地点
关于表单提交。
例如
Place place = myDatabaseService.findPlace(country, name ...) //does not account for misspellings
if(place == null){
place = new Place(tripCountryField.getText(), tripCityField.getText(), tripIslandField.getText(), tripPlaceInfoTextArea.getText());
}
我认为您的实体需要覆盖 hashcode()
和 equals()
方法才能识别它们的唯一性。
我有 2 个 tables:旅行和地点(多对一),我的问题是当我添加 2 个数据不同但地点相同的旅行时,它会向 Trip 添加 2 条记录table 和 2 条记录到 Place table,而它应该只添加 1 条记录到 Place。
例如,我有 2 次不同日期的旅行,但它们都在同一个地方 - 意大利、罗马。所以这些数据应该只有一个记录:意大利,罗马。
如何在我的应用程序中避免此类行为?
行程class:
public class Trip implements java.io.Serializable {
private int idTrip;
private int idHotel;
private Date date;
private int cost;
private int profit;
private String organisator;
private int period;
private String food;
private String transport;
private int persons;
private int kidsAmount;
private String ownerName;
private String ownerLastName;
private Place place;
+ 构造函数、get() 和 set() 方法,
地点class:
public class Place implements java.io.Serializable {
private int idPlace;
private String country;
private String city;
private String island;
private String information;
private Set<Trip> trips;
+ 构造函数、get() 和 set() 方法,
行程映射文件:
<hibernate-mapping>
<class name="pl.th.java.biuro.hibernate.Trip" table="Trip">
<id column="idTrip" name="idTrip" type="int">
<generator class="native"/>
</id>
<property column="date" name="date" type="date"/>
<property column="cost" name="cost" type="int"/>
<property column="profit" name="profit" type="int"/>
<property column="organisator" name="organisator" type="string"/>
<property column="period" name="period" type="int"/>
<property column="food" name="food" type="string"/>
<property column="transport" name="transport" type="string"/>
<property column="persons" name="persons" type="int"/>
<property column="kidsAmount" name="kidsAmount" type="int"/>
<property column="idHotel" name="idHotel" type="int"/>
<many-to-one fetch="select" name="place" class="pl.th.java.biuro.hibernate.Place">
<column name="idPlace" not-null="true"></column>
</many-to-one>
</class>
</hibernate-mapping>
放置映射文件:
<hibernate-mapping>
<class name="pl.th.java.biuro.hibernate.Place" table="Place">
<id column="idPlace" name="idPlace" type="int">
<generator class="native"/>
</id>
<property column="country" name="country" type="string"/>
<property column="city" name="city" type="string"/>
<property column="island" name="island" type="string"/>
<property column="information" name="information" type="string"/>
<set name="trips" table="Trip" inverse="true" lazy="true" fetch="select">
<key>
<column name="idPlace" not-null="true" />
</key>
<one-to-many class="pl.th.java.biuro.hibernate.Trip" />
</set>
</class>
</hibernate-mapping>
我还用我的 MySQL 数据库添加了一些屏幕截图,也许有一些问题不允许我正确地执行此操作: MySQL database
编辑: 在 Place 映射文件和 Set in Place 中添加一对多关系 class,但仍然遇到同样的问题。
EDIT2: 将具有持久实体的代码添加到数据库中:
Session session = DatabaseConnection.getFactory().openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
trip.setPlace(place);
session.save(place);
session.save(trip);
tx.commit();
} catch (HibernateException e) {
if (tx != null) {
tx.rollback();
}
System.out.println("Exception found while adding new trip: " + e.getMessage());
e.printStackTrace();
} finally {
session.close();
}
但我仍然遇到同样的问题...我添加了一个地点 A 的行程,然后在下一步中我添加了另一个相同的行程,这是我得到的:
EDIT3: 创建 Trip 和 Place 对象:
Trip trip = null;
Place place = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateInString = tripDateField.getText();
java.util.Date date = null;
try {
date = sdf.parse(dateInString);
place = new Place(tripCountryField.getText(), tripCityField.getText(), tripIslandField.getText(), tripPlaceInfoTextArea.getText());
int period = 0, persons = 0, kidsAmount = 0;
//W razie braku niewymaganych liczbowych danych ustawiane są wartości -1
if (tripPeriodField.getText().equals("")) {
period = -1;
}
if (tripPersonsField.getText().equals("")) {
persons = -1;
}
if (tripKidsAmountField.getText().equals("")) {
kidsAmount = -1;
}
trip = new Trip(new Date(date.getTime()), Integer.parseInt(tripCostField.getText()), Integer.parseInt(tripProfitField.getText()),
tripOrganisatorField.getText(), period, tripFoodField.getText(), tripTransportField.getText(),
persons, kidsAmount, tripClientNameField.getText(), tripClientLastNameField.getText());
} catch (ParseException ex) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
try {
date = sdf.parse("0000-00-00");
} catch (ParseException ex1) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex1);
}
} catch (NumberFormatException e) {
place = new Place("111", "111", "111", "111");
trip = new Trip(null, WIDTH, WIDTH, dateInString, WIDTH, dateInString, dateInString, WIDTH, ABORT, dateInString, dateInString);
System.out.println("Exception while getting trip / place data: " + e.toString());
}
dataEdition.addTrip(trip, place, dataEdition.validateTripData(trip, place), addRemoveSearchTripDialog);
我从 textFields 获取这些对象数据并在需要时将它们解析为 int,所以我想这应该没问题。在此之后,我将这两个对象传递到另一个方法中,我将它们保存到数据库中。
我试过这个,没有重复:
并在数据库中创建两个 classes 和 tables,我在与 classes 相同的包中创建了两个映射文件。
地点映射:
<hibernate-mapping package="pl.th.java.biuro.hibernate">
<class name="Place" table="PLACE">
<id name="idPlace" type="java.lang.Long" column="ID_PLACE">
<generator class="native">
</generator></id>
<set name="trips" table="Trip" inverse="true" lazy="true" fetch="select">
<key>
<column name="idPlace" not-null="true">
</column></key>
<one-to-many class="pl.th.java.biuro.hibernate.Trip">
</one-to-many></set>
</class>
</hibernate-mapping>
以及行程映射:
<hibernate-mapping package="pl.th.java.biuro.hibernate">
<class name="Trip" table="TRIP">
<id name="idTrip" type="java.lang.Long" column="ID_TRIP">
<generator class="native">
</generator></id>
<many-to-one name="place" class="pl.th.java.biuro.hibernate.Place" fetch="select">
<column name="idPlace" not-null="true">
</column></many-to-one>
</class>
</hibernate-mapping>
我看到,您的行程 ID 不是在您的 table 上生成的?那是对的吗?而且您的命名约定有点容易出错。尝试将 db tables 命名为大写并将 class 中的 set 变量命名为 maybe trips。在您的映射中,您将 id 映射到一个 int。大多数数据库将其映射为长,所以可能也有错误。
您似乎在每次提交时都创建了一个 NEW 地点。
Place place = new Place(tripCountryField.getText(), tripCityField.getText(), tripIslandField.getText(), tripPlaceInfoTextArea.getText());
然后您希望 Hibernate 以某种方式神奇地 确定您实际上可能想要使用数据库中的现有条目。
这行不通。
如果提交的地点已经存在,那么您需要以某种方式加载现有的持久实体并使用它。
您可以通过允许用户 select 现有地点并通过该地点的 ID 发送来在前端解决此问题,或者查询匹配的地点 关于表单提交。
例如
Place place = myDatabaseService.findPlace(country, name ...) //does not account for misspellings
if(place == null){
place = new Place(tripCountryField.getText(), tripCityField.getText(), tripIslandField.getText(), tripPlaceInfoTextArea.getText());
}
我认为您的实体需要覆盖 hashcode()
和 equals()
方法才能识别它们的唯一性。