2009-04-20 14 views
46

È possibile creare una tabella (da Hibernate con annotazione JPA @Entity) che non contiene una chiave primaria/Id?Sospensione e no PK

So che questa non è una buona idea; una tabella dovrebbe avere una chiave primaria.

+0

È possibile creare una chiave surrogata che viene inserita automaticamente da 'Sequenza' (o equivalente se non si utilizza Oracle). Per i vecchi dati è possibile popolare la chiave appena creata con numeri in esecuzione. –

risposta

10

Ho scoperto che non è possibile farlo. Quindi sfortuna per chi lavora con sistemi legacy.

Se si esegue il reverse engineering (creare entità annotate JPA dalla connessione JDBC esistente) la tabella creerà due classi Java, una Entità e un campo; id e un id incorporabile contenente tutte le colonne della tua relazione.

+0

Davvero? Che dire della soluzione di @ awied sotto? WFM. – javamonkey79

+0

javamonkey79, la soluzione di @ awied e la soluzione di cui sopra sono la stessa cosa. –

49

L'auto-risposta di Roger è corretta. Per elaborare un po 'su quello che si intende (non sono stato chiaro su di esso in un primo momento e pensato che questo avrebbe aiutato):

dire che hai di avere un tavolo Foo come tale:

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

Normalmente, puoi scrivere una classe con annotazioni per lavorare con questa tabella:

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

} 

.. Ma, dannazione. Questa tabella non ha nulla che possiamo usare come id, ed è un database legacy che usiamo per [inserire la funzione business vitale]. Non penso che mi lasceranno iniziare a modificare i tavoli per poter utilizzare l'ibernazione.

È possibile, invece, suddividere l'oggetto in una struttura di tipo hibernate-workable che consente di utilizzare l'intera riga come chiave. (Naturalmente, questo presuppone che la riga è unico.)

Spalato l'oggetto Foo in due così:

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

    @Id 
    private FooKey id; 

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

    public void getId() { 
    return id; 
    } 
} 

e

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

}

.. E che dovrebbe essere esso. Hibernate utilizzare il tasto integrato per la sua identità richiesto e si può effettuare una chiamata normale:

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

Spero che questo aiuti esordienti con ottenere questo lavoro.

+16

Volevo menzionare, perché ho appena avuto questo problema, che se qualcuno di quelle colonne che stai usando per la tua chiave composita sono NULL, Hibernate restituirà oggetti nulli. Stavo tirando tutto da una vista e una delle colonne era tutto nullo, quindi ibernare stava restituendo una lista con il giusto numero di elementi, ma tutti gli elementi erano nulli. Nel mio caso non ho potuto modificare il campo, quindi l'ho appena rimosso dalla chiave. – Casey

3

Non è necessario creare una classe separata per essere il tuo @Id o chiave primaria. Basta usare un numero intero (o qualsiasi altra cosa). Inoltre, non pubblicare la chiave falsa in quanto gli sviluppatori che la utilizzano potrebbero pensare che sia reale e in ogni caso provare a utilizzarla. Infine, questo è meglio utilizzato in una VISTA. Concordo con i post precedenti che nella maggior parte, se non in tutti i casi, le tabelle dovrebbero avere una chiave primaria. Ad esempio:

@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

Questo non funziona. Ho trovato un errore nella colonna non trovato dopo averlo aggiunto. – Jeremy

+0

Questo funziona. Devi farlo su una vista. Inoltre, ovviamente, cambia i membri in colonne del tuo database. Se hai usato "realColumn", ovviamente non funzionerà. Ho usato questo approccio in un sistema reale. –

+3

Ottenere un org.hibernate.HibernateException: colonna mancante: id in View_Blabla quando provo a utilizzare questo in una vista – StefanTo

37

Utilizzare il seguente codice; Hibernate non ha una sua logica di distinguere i record duplicati

Fatemi sapere se ci sono problemi con questo approccio

@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

Questa è la migliore risposta. Si utilizza come classe ID senza dover fare due classi separate. Ho spostato le annotazioni di identificazione sui miei getter in quanto disponevano dei necessari mapping di colonne. Funziona perfettamente e non interrompe il caching. +1 –

+1

@JohnStrickler: c'è qualche inconveniente di questo approccio? Sembra più un hack –

+0

Finora non ne ho trovato nessuno. Hai solo bisogno di annotare le proprietà che sono uniche (che potrebbero essere tutte quante se nessuna è unica). Non lo classificherei come un hack: sta usando le annotazioni JPA nel modo in cui sono state pensate per essere utilizzate. –

1

Per creare il Pojo da tavolo - utilizzare il metodo di ingegneria inversa esistente in Eclipse. Per la tabella delle chiavi non primaria, eclipse genererà le due classi 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()); 
      } 
     }  
} 
1

Aggiunta al commento di Awied. Se poi si desidera cercare una barra, utilizzare il seguente HQL.

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

ho trovato che questo trucco funziona:

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

Ti rocce così tanto! Questa era una soluzione perfetta! –

+0

Dove devo mantenere il codice di cui sopra –

+0

nel file hbm.xml all'inizio come la definizione di una chiave primaria invece – RudiDudi

0

Ho trovato la soluzione per le tabelle senza chiave primaria e nulla come valori. Funzionerà su Oracle DB. Forse qualcosa di simile esiste per altri DB.

  1. Si dovrebbe creare una nuova chiave primaria nella classe POJO:

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

e utilizzare createNativeQuery come questo

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

La query nativo genererà chiave primaria e si ottengono risultati unici.

1

Quando si tratta di visualizzazioni invece di cercare soluzioni alternative in Hibernate, potrebbe essere più semplice aggiungere un id fittizio nella vista del database. Ho scritto su di esso in un'altra domanda: https://stackoverflow.com/a/44050230/1673775

Problemi correlati