#java #hibernate #hibernate-mapping
Вопрос:
Я работаю над приложением, которое должно поддерживать Employees
и Customers
. У Customers
кого есть Address
, Employees
у кого нет. Я не хочу создавать для них две отдельные таблицы в базе данных. В базе данных у меня есть a users
, a roles
и users_roles
таблица, которая указывает, является ли пользователь Сотрудником или Клиентом.
Вот что у меня происходит:
users
------
id
first_name
last_name
credentials_id (the username from `credentials`)
credentials
------
username
password
addresses
------
id
city
country
street
users_addresses
------
user_id
address_id
И вот как выглядят отображения в режиме гибернации:
@Entity
@Table(name = "users")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "firstName", column = @Column(name = "first_name")),
@AttributeOverride(name = "lastName", column = @Column(name = "last_name"))
})
private UserDetails userDetails;
@ManyToOne
@JoinTable(name = "customers_addresses",
joinColumns = {@JoinColumn(name = "user_id")},
inverseJoinColumns = {@JoinColumn(name = "address_id")})
private Address address;
}
@Entity
@Table(name = "users")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "firstName", column = @Column(name = "first_name")),
@AttributeOverride(name = "lastName", column = @Column(name = "last_name"))
})
private UserDetails userDetails;
}
@Embeddable
public class UserDetails {
private String firstName;
private String lastName;
@OneToOne
@JoinColumn(name = "credentials_id")
private UserCredentials credentials;
}
Все это работает нормально, но когда я запрашиваю сотрудников (у createQuery("from Employee")
них есть address
поле на них, то есть null
. Кроме того, когда я запрашиваю клиентов (с createQuery("from Customers")
, результат также включает сотрудников.
Есть какие-нибудь идеи?
Комментарии:
1.
Employee
у него нет поляaddress
, вы ожидаете, что оно будет установлено?2. Я ожидаю, что запуск
createQuery("from Employee")
не вернет объект с anaddress
.3. CreateQuery(«от сотрудника»).getResultList(); он вернет a
List<Employee>
.address
нигде не появится. Я не уверен, что это ваше сомнение или что-то, что на самом деле с вами происходит.
Ответ №1:
У меня есть a
users
, aroles
иusers_roles
таблица, которая указывает, является ли пользователь aEmployee
или aCustomer
.
Могут Employee
ли s и Customer
s поменяться ролями?
Если нет, то наиболее разумным решением было бы использовать наследование:
@Entity
@Inheritance(SINGLE_TABLE)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Embedded
private UserDetails userDetails;
}
@Entity
public class Customer extends User {
@ManyToOne
@JoinTable(name = "customers_addresses",
joinColumns = {@JoinColumn(name = "user_id")},
inverseJoinColumns = {@JoinColumn(name = "address_id")})
private Address address;
}
@Entity
public class Employee extends User {}
При вышеуказанной настройке from Employees
будут возвращены только сотрудники, from Customers
— клиенты и from Users
будут выбраны все User
s. Недостатком, конечно, является избыточная информация в виде столбца-дискриминатора.
Ответ №2:
Предполагая, что вы не хотите использовать наследование, вам, вероятно, понадобится @Filter
:
@Entity
@Table(name="users")
@SecondaryTable(
name = "user_roles",
pkJoinColumns = @PrimaryKeyJoinColumn(name = "user_id")
)
@FilterDef(
name="filterByRole",
parameters = @ParamDef( name="roleId",type="int" )
)
@Filter(
name="filterByRole",
condition="{ur}.role_id = :roleId",
aliases = @SqlFragmentAlias( alias = "ur", table= "user_roles")
)
class Employee {
}
Недостатком является то, что вам нужно включить его перед запросом:
entityManager
.unwrap( Session.class )
.enableFilter( "filterByRole" )
.setParameter( "roleid", employeeRoleId);
List<Employee> employees = entityManager
.createQuery("from Employee", Employee.class)
.getResultList();
См. раздел фильтры в документации по Hibernate ORM
Существует также @Where
аннотация, но я не думаю, что она будет работать в вашем случае, если вы не сможете фильтровать, используя только столбцы в таблице users
.
Комментарии:
1. «от сотрудника верните объект с адресом, потому что вы не указали класс сущности» — ну, не совсем так. Он возвращает объекты с адресами, потому что JPA не может знать, что объект с адресом не должен быть сотрудником. Это просто предполагает, что все содержимое таблицы представляет сотрудников, поскольку в отображении нет ничего, что указывало бы на обратное.
2. Что-то не так, когда Hibernate ORM преобразует форму запроса HQL в SQL, он добавит необходимые столбцы в SQL. У сотрудника нет столбца адреса, поэтому он не должен отображаться в предложении SQL select. Я не понимаю, зачем добавлять столбец адреса, поскольку это не поле сотрудника.
3. Я удалил этот комментарий о передаче сущности