2009-04-20 6 views
46

Ist es möglich, eine Tabelle zu erstellen (aus einer JPA Hibernate kommentierte @Entity), die keinen Primärschlüssel/Id enthält?Hibernate und keine PK

Ich weiß, dass dies keine gute Idee ist; Eine Tabelle sollte einen Primärschlüssel haben.

+0

Sie könnten einen Ersatzschlüssel erstellen, der automatisch aus 'Sequence' eingefügt wird (oder gleichwertig, wenn Sie Oracle nicht verwenden). Bei alten Daten könnten Sie den neu erstellten Schlüssel mit laufenden Nummern füllen. –

Antwort

10

fand ich, dass es nicht möglich, dies zu tun. So viel Pech für diejenigen, die mit Legacy-Systemen arbeiten.

Wenn Sie Reverse Engineering (erstellen PPV kommentierte Einheiten aus vorhandener JDBC-Verbindung) wird die Tabelle zwei Java-Klassen erstellen, eine Entity und mit einem Feld; ID und eine eingebettete ID, die alle Spalten aus Ihrer Beziehung enthält.

+0

Wirklich? Was ist mit der Lösung von @awied unten? Es WFM. – javamonkey79

+0

javamonkey79, @ awarded Lösung und obige Lösung sind das Gleiche. –

49

Rogers Selbst-Antwort ist korrekt. Um ein wenig zu erarbeiten, was gemeint ist (ich war nicht klar, es auf den ersten und dachte das würde helfen):

Angenommen, Sie haben eine Tabelle Foo als solche haben:

TABLE Foo (
bar varchar(20), 
bat varchar(20) 
) 

Normalerweise Sie kann eine Klasse mit Annotationen schreiben, um mit dieser Tabelle zu arbeiten:

// 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;  
    } 

} 

.. Aber, verdammt. Diese Tabelle enthält nichts, was wir als ID verwenden können, und es ist eine Legacy-Datenbank, die wir für [wichtige Geschäftsfunktion einfügen] verwenden. Ich glaube nicht, dass sie mir erlauben werden, Tabellen zu ändern, damit ich Hibernate verwenden kann.

können Sie spalteten stattdessen das Objekt in eine Ruhezustand-bearbeitbaren Struktur, die bis die gesamte Zeile als Taste verwendet werden. (Natürlich setzt dies voraus, dass die Zeile eindeutig ist.)

Split die Foo Objekt in zwei thusly:

@Entity 
@Table(name = "FOO") 
class Foo { 

    @Id 
    private FooKey id; 

    public void setId(FooKey id) { 
    this.id = id; 
    } 

    public void getId() { 
    return id; 
    } 
} 

und

@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;  
    } 

}

.. Und das sollte es. Hibernate die Fähiges Schlüssel für seine erforderliche Identität verwenden, und Sie telefonieren normal machen kann:

Query fooQuery = getSession().createQuery("from Foo"); 

this helps Erstbesucher mit diesen Arbeiten zu bekommen.

+16

Ich wollte erwähnen, da ich gerade dieses Problem hatte, dass, wenn eine dieser Spalten, die Sie für Ihren zusammengesetzten Schlüssel verwenden, NULL sind, als Hibernate Nullobjekte zurückgibt. Ich habe alles aus einem View gezogen und eine der Spalten war null, also überwachte Hibernate eine Liste mit der richtigen Anzahl von Elementen, aber alle Elemente waren null. In meinem Fall konnte ich das Feld nicht ändern, also habe ich es einfach aus dem Key entfernt. – Casey

3

Sie benötigen keine separate Klasse erstellen Ihre @Id oder Primärschlüssel zu sein. Verwenden Sie einfach eine Ganzzahl (oder was auch immer). Veröffentlichen Sie den gefälschten Schlüssel auch nicht, da Entwickler, die ihn verwenden, glauben könnten, dass er echt ist und versuchen Sie, ihn zu verwenden. Zuletzt wird dies am besten in einer Ansicht verwendet. Ich stimme früheren Beiträgen zu, dass in den meisten, wenn nicht in allen Fällen, Tabellen einen Primärschlüssel haben sollten. Zum Beispiel:

@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;  
    } 


} 
+3

Dies funktioniert nicht. Nach dem Hinzufügen wurde ein Fehler in der Spalte nicht gefunden gefunden. – Jeremy

+0

Das funktioniert. Sie müssen es auf einer Ansicht tun. Ändern Sie natürlich auch die Mitglieder in Spalten Ihrer Datenbank. Wenn Sie "realColumn" verwendet haben, wird es natürlich nicht funktionieren. Ich habe diesen Ansatz in einem realen System verwendet. –

+3

Abrufen einer org.hibernate.HibernateException: Fehlende Spalte: ID in View_Blabla, wenn ich versuche, dies in einer Ansicht zu verwenden – StefanTo

37

Verwenden Sie den folgenden Code; Hibernate keine eigene Logik haben, um doppelte Datensätze zu unterscheiden

Lassen Sie mich wissen, ob es irgendwelche Probleme mit diesem Ansatz sind

@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;  
    } 
} 
+2

Dies ist die beste Antwort. Es verwendet sich selbst als die ID-Klasse ohne zwei getrennte Klassen zu haben. Ich habe die Id-Annotationen in meine Getters verschoben, da sie die benötigten Spalten-Mappings hatten. Es funktioniert perfekt und es bricht nicht Caching. +1 –

+1

@ JohnStrickler: Gibt es einen Nachteil dieses Ansatzes? Es sieht eher aus wie ein Hack –

+0

Bisher habe ich keine gefunden. Sie müssen nur die Eigenschaften annotieren, die eindeutig sind (das können alle sein, wenn keine eindeutig sind). Ich würde es nicht als Hack einstufen - es verwendet JPA-Annotationen so, wie sie verwendet werden sollten. –

1

die Pojo von Tabelle zu erstellen - mit dem Reverse-Engineering-Verfahren in Eclipse existiert. Für die Tabelle ohne Primärschlüssel generiert Eclipse die beiden Pojo-Klassen.

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()); 
      } 
     }  
} 
1

Hinzufügen zu Awieds Kommentar. Wenn Sie dann nach einer Leiste suchen möchten, verwenden Sie die folgende HQL.

Query fooQuery = getSession().createQuery("from Foo.id.bar = '<barName>'"); 
6

fand ich, dass dieser Trick funktioniert:

<id column="ROWID" type="string" /> 
+0

Sie rockt so hart! Das war eine perfekte Lösung! –

+0

Wo ich den obigen Code –

+0

in der Datei hbm.xml am Anfang wie die Definition eines Primärschlüssels statt – RudiDudi

0

ich für Tabellen ohne Primärschlüssel und null als Werte gefunden Lösung haben. Es wird auf Oracle DB funktionieren. Vielleicht gibt es etwas ähnliches für andere DBs.

  1. Sie sollten neue Primärschlüssel in der POJO Klasse erstellen:

    @Id @Column (name = "id") privaten Integer id;

und verwenden createNativeQuery wie diese

getEntityManager().createNativeQuery("select rownum as id, ..... 

Die native Abfrage Primärschlüssel generieren und finden Sie einzigartige Ergebnisse.

1

Wenn es um Ansichten geht, anstatt nach Workarounds in Hibernate zu suchen, kann es einfacher sein, eine Dummy-ID in der Datenbankansicht hinzuzufügen. Ich habe darüber in einer anderen Frage geschrieben: https://stackoverflow.com/a/44050230/1673775

Verwandte Themen