Спящий и без ПК - PullRequest
       4

Спящий и без ПК

52 голосов
/ 20 апреля 2009

Можно ли создать таблицу (из аннотации Hibernate JPA @Entity), которая не содержит первичный ключ / идентификатор?

Я знаю, что это не очень хорошая идея; таблица должна иметь первичный ключ.

Ответы [ 10 ]

50 голосов
/ 21 ноября 2009

Правильный ответ Роджера. Чтобы немного пояснить, что имеется в виду (сначала я не понял этого, подумал, что это поможет):

Скажем, у вас есть таблица 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");

Надеюсь, это поможет начинающим с этим работать.

45 голосов
/ 29 сентября 2011

Используйте следующий код; 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;    
  }
}
9 голосов
/ 19 мая 2016

я обнаружил, что этот трюк работает:

<id column="ROWID" type="string" />
9 голосов
/ 29 апреля 2009

Я обнаружил, что это невозможно. Так что везет тем, кто работает с устаревшими системами.

Если вы перепроектируете (создаете аннотированные сущности JPA из существующего соединения JDBC), таблица создаст два Java-класса, один Entity и с одним полем; идентификатор и один встраиваемый идентификатор, содержащий все столбцы из вашего отношения.

2 голосов
/ 25 мая 2011

Вам не нужно создавать отдельный класс, который будет вашим @Id или Первичным ключом. Просто используйте Integer (или что-то еще). Кроме того, не публикуйте поддельный ключ, так как разработчики, которые его используют, могут подумать, что он настоящий, и в противном случае пытаются его использовать. Наконец, это лучше всего использовать в виде. Я согласен с более ранними сообщениями, что в большинстве, если не во всех случаях, таблицы должны иметь первичный ключ. Например:

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


}
1 голос
/ 18 мая 2017

Когда дело касается представлений вместо поиска обходных путей в Hibernate, может быть проще добавить фиктивный идентификатор в представление базы данных. Я написал об этом в другом вопросе: https://stackoverflow.com/a/44050230/1673775

1 голос
/ 26 марта 2015

Добавление к комментарию Awied. Если вы хотите найти бар, используйте следующий HQL.

Query fooQuery = getSession().createQuery("from Foo.id.bar = '<barName>'");
1 голос
/ 21 июля 2014

Чтобы создать 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());
            }
        }      
}
0 голосов
/ 26 февраля 2019

Несмотря на то, что ROWID является псевдостолбцом, все же, поскольку ROWID соответствует физическому адресу строки, это самый быстрый способ получить любые данные строки. Поскольку @Id используется для уникальной идентификации объекта, а ROWID является уникальным внутри таблицы, мы можем использовать его для преодоления обсуждаемой нами проблемы. На самом деле, если нам не нужен какой-либо значимый уникальный идентификатор, ROWID - это лучший столбец для аннотации @Id, поскольку он соответствует физическому адресу строки. Следующий код работал для меня. @Id @Column(name = "ROWID") private String Id;

0 голосов
/ 08 сентября 2016

Я нашел решение для таблиц без первичного ключа и пустых значений. Это будет работать на оракул БД. Может быть, что-то подобное существует для других БД.

  1. Вы должны создать новый первичный ключ в классе POJO:

    @ Id @Column (имя = "ID") закрытый целочисленный идентификатор;

и используйте createNativeQuery, как этот

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

Собственный запрос сгенерирует первичный ключ, и вы получите уникальные результаты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...