Спящий и без ПК
Можно ли создать таблицу (из аннотированного JPA Hibernate @Entity
) что не содержит первичный ключ / Id?
Я знаю, что это не очень хорошая идея; таблица должна иметь первичный ключ.
10 ответов
Я обнаружил, что это невозможно. Так что везет тем, кто работает с устаревшими системами.
Если вы перепроектируете (создаете аннотированные сущности JPA из существующего соединения JDBC), таблица создаст два Java-класса, один Entity и с одним полем; идентификатор и один встраиваемый идентификатор, содержащий все столбцы из вашего отношения.
Самоответ Роджера правильный. Чтобы немного уточнить, что имеется в виду (сначала я не понял этого, и подумал, что это поможет):
Скажем, у вас есть таблица Foo как таковая:
TABLE Foo (
bar varchar(20),
bat varchar(20)
)
Обычно вы можете написать класс с аннотациями для работы с этой таблицей:
// Technically, for this example, the @Table and @Column annotations
// are not needed, but don't hurt. Use them if your column names
// are different than the variable names.
@Entity
@Table(name = "FOO")
class Foo {
private String bar;
private String bat;
@Column(name = "bar")
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
@Column(name = "bat")
public String getBat() {
return bat;
}
public void setBat(String bat) {
this.bat = bat;
}
}
.. Но, черт возьми. В этой таблице нет ничего, что мы могли бы использовать в качестве идентификатора, и это устаревшая база данных, которую мы используем для [вставить жизненно важную бизнес-функцию]. Я не думаю, что они позволят мне начать изменять таблицы, чтобы я мог использовать спящий режим.
Вместо этого вы можете разбить объект на гибернацию, которая позволяет использовать всю строку в качестве ключа. (Естественно, это предполагает, что строка уникальна.)
Разделите объект Foo на два так:
@Entity
@Table(name = "FOO")
class Foo {
@Id
private FooKey id;
public void setId(FooKey id) {
this.id = id;
}
public void getId() {
return id;
}
}
а также
@Embeddable
class FooKey implements Serializable {
private String bar;
private String bat;
@Column(name = "bar")
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
@Column(name = "bat")
public String getBat() {
return bat;
}
public void setBat(String bat) {
this.bat = bat;
}
}
.. И это должно быть так. Hibernate будет использовать ключ Embeddable для своей требуемой личности, и вы можете сделать вызов как обычно:
Query fooQuery = getSession().createQuery("from Foo");
Надеюсь, что это поможет начинающим с этим работать.
Используйте следующий код; Hibernate не имеет своей собственной логики, чтобы различать дубликаты записей
Дайте мне знать, если есть какие-либо проблемы с этим подходом
@Entity @IdClass(Foo.class)
class Foo implements Serializable {
@Id private String bar;
@Id private String bat;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
public String getBat() {
return bat;
}
public void setBat(String bat) {
this.bat = bat;
}
}
Я обнаружил, что этот трюк работает:
<id column="ROWID" type="string" />
Хотя ROWID является псевдоколонкой, тем не менее, поскольку ROWID соответствует физическому адресу ROW, это самый быстрый способ получить данные любой строки. Поскольку @Id используется для уникальной идентификации объекта, а ROWID уникален внутри таблицы, мы можем использовать его для решения проблемы, которую мы обсуждаем. Фактически, если нам не нужен какой-либо значимый уникальный идентификатор, ROWID - лучший столбец для аннотации с аннотацией @Id, поскольку он соответствует физическому адресу строки. У меня сработал следующий код.
@Id
@Column(name = "ROWID")
private String Id;
Когда дело доходит до представлений вместо поиска обходных путей в Hibernate, может быть проще добавить фиктивный идентификатор в представление базы данных. Я написал об этом в другом вопросе: /questions/22082695/spyaschij-rezhim-postoyanstvo-bez-id/22082710#22082710
Вам не нужно создавать отдельный класс, который будет вашим @Id или Первичным ключом. Просто используйте Integer (или что-то еще). Кроме того, не публикуйте поддельный ключ, так как разработчики, которые его используют, могут подумать, что он настоящий, и в противном случае пытаются его использовать. Наконец, это лучше всего использовать в VIEW. Я согласен с предыдущими сообщениями, что в большинстве, если не во всех случаях, таблицы должны иметь первичный ключ. Например:
@Entity
@Table(name = "FOO")
class Foo {
@SuppressWarnings("unused")
@Id
private Integer id;
@Column(name = "REAL_COLUMN")
private String realColumn;
public String getRealColumn() {
return realColumn;
}
public void setRealColumn(String realColumn) {
this.realColumn= realColumn;
}
}
Добавление к комментарию Awied. Если вы хотите найти бар, используйте следующий HQL.
Query fooQuery = getSession().createQuery("from Foo.id.bar = '<barName>'");
Чтобы создать Pojo из таблицы - используйте метод обратного инжиниринга, существующий в eclipse. Для таблицы не первичного ключа eclipse сгенерирует два класса Pojo.
eclipse generated class and hbm.xml -
---
Foo.java
//
public class Foo implements java.io.Serializable {
private FooId id;
public Foo() {
}
public Foo(FooId id) {
this.id = id;
}
public FooId getId() {
return this.id;
}
public void setId(FooId id) {
this.id = id;
}
}
---
FooId.java
//
public class FooId implements java.io.Serializable {
private String bar;
private String bat;
public FooId() {
}
public FooId(String bar, String bat) {
this.bar = bar;
this.bat = bat;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
public String getBat() {
return this.bat;
}
public void setBat(String bat) {
this.bat = bat;
}
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof FooId))
return false;
FooId castOther = (FooId) other;
return ((this.getBar() == castOther.getBar()) || (this.getBar() != null
&& castOther.getBar() != null && this.getBar().equals(
castOther.getBar())))
&& ((this.getBat() == castOther.getBat()) || (this.getBat() != null
&& castOther.getBat() != null && this.getBat().equals(
castOther.getBat())));
}
public int hashCode() {
int result = 17;
result = 37 * result
+ (getBar() == null ? 0 : this.getBar().hashCode());
result = 37 * result
+ (getBat() == null ? 0 : this.getBat().hashCode());
return result;
}
}
---
Foo.hbm.xml
<hibernate-mapping>
<class name="com.Foo" table="foo" schema="public" catalog="hibernate_poc">
<composite-id name="id" class="com.FooId">
<key-property name="bar" type="string">
<column name="bar" length="20" />
</key-property>
<key-property name="bat" type="string">
<column name="bat" length="20" />
</key-property>
</composite-id>
</class>
</hibernate-mapping>
---
entry in the Hibernate.cfg.xml -
<mapping class="com.poc.Foo" resource="Foo.hbm.xml"/>
---
Fetch the Data from table -
FooDataFetch.java
//
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class FooDataFetch {
private static Session session = null;
public static void main(String[] args) {
try{
Configuration cfg = new Configuration();
cfg.configure("/hibernate.cfg.xml");
SessionFactory sf = cfg.buildSessionFactory();
session = sf.openSession();
session.beginTransaction();
queryPerson(session);
session.close();
}catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
ex.printStackTrace();
throw new ExceptionInInitializerError(ex);
}
}
private static void queryPerson(Session session) {
Query query = session.createQuery("from Foo");
List <Foo>list = query.list();
java.util.Iterator<Foo> iter = list.iterator();
while (iter.hasNext()) {
Foo foo = iter.next();
System.out.println("Foo Details: \"" + foo.getId().getBar() +"\", " + foo.getId().getBat());
}
}
}
Я нашел решение для таблиц без первичного ключа и NULL в качестве значений. Это будет работать на оракул БД. Может быть, что-то подобное существует для других БД.
Вы должны создать новый первичный ключ в классе POJO:
@Id @Column (name = "id") private Integer id;
и использовать createNativeQuery, как это
getEntityManager().createNativeQuery("select rownum as id, .....
Собственный запрос сгенерирует первичный ключ, и вы получите уникальные результаты.