@mrkovalchuk
Junior developer

Как исправить Null id generated?

Добрый день. Имею следующую проблему:
org.hibernate.id.IdentifierGenerationException: null id generated for:class Notebook.Enities.Telephone


Суть понятна, дебаг подтверждает: в id у нас null у telephone.

GitHub: Notebook
СУБД MySQL. Поле Id: Id int(11) not null;

Класс Note.java
@Component
public class Note {
    private int id;
    private Description descriptionId;
    private Human humanId;
    private Telephone telephoneId;
    private Group group;

    public Note (){

    }

    public Note(int id, Description descriptionId, Human humanId, Telephone telephoneId, Group group) {
        this.id = id;
        this.descriptionId = descriptionId;
        this.humanId = humanId;
        this.telephoneId = telephoneId;
        this.group = group;
    }

/************сеттеры и геттеры*****************/


Класс Telephone.java

@Component
public class Telephone {

    private Integer id;
    private String telephone;
    private String descripton;
    private Note note;

    public Note getNote() {
        return note;
    }

    public void setNote(Note note) {
        this.note = note;
    }

    public Telephone(int id, String telephone, String descripton) {
        this.id = id;
        this.telephone = telephone;
        this.descripton = descripton;
    }

    public Telephone() {
    }

    public Telephone(String telephone) {
        this.telephone = telephone;
    }
/*******************геттеры и сэттеры*************/


Action-исполнитель: NotebookFacade.java

package Notebook.Objects;


import Notebook.DAO.interfaces.getNoteDAO;
import Notebook.Enities.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
@Scope("singleton")
public class NotebookFacade {

    @Autowired
    private getNoteDAO getNoteDAO;

    @Autowired
    private SearchCriteria searchCriteria;

    private List<Note> notes;


    public void createNote(Human human, Telephone telephone, Group group, Description description){
        Note note = new Note();
        note.setTelephoneId(telephone);
            telephone.setNote(note);
        note.setHumanId(human);
            human.setNote(note);
        note.setGroup(group);
        note.setDescriptionId(description);
            description.setNote(note);
        getNoteDAO.addNotes(note);
        System.out.println("Add work!");
    }

   

}


И сам исполнитель, который и ругается.
package Notebook.DAO.impl;

import Notebook.DAO.interfaces.getNoteDAO;
import Notebook.Enities.*;
import Notebook.Utils.HibernateUtil;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.*;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * Created by Ковальчук on 30.03.2016.
 */
@Component
public class GetNoteDAOImpl implements getNoteDAO {

    @Autowired
    private SessionFactory sessionFactory;
    private ProjectionList noteProjectionList;



     /******************************      Методы получения       *********************************/

       /****************************** Методы удаления, изменения записей *********************************/
    @Transactional
    @Override
    public void addNotes(Note note) {
        Session session = HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
        session.save(note);
        session.getTransaction().commit();
        System.out.println("This function is worked");
        session.close();
    }


    @Transactional
    @Override
    public void deleteNotes(Note note) {
            Session session = HibernateUtil.getSessionFactory().openSession();
            session.beginTransaction();
            session.delete(note.getId());
            session.flush();
            session.getTransaction().commit();
    }
   
}


Telephone.hbm.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>

    <class name="Notebook.Enities.Telephone" table="telephone" schema="notebase">
        <id name="id" type="java.lang.Integer">
            <column name="Id"/>
            <generator class="foreign">
                <param name="property">note</param>
            </generator>
        </id>
        <one-to-one name="note" class="Notebook.Enities.Note" constrained="true"/>
        <property name="telephone" column="Telephone"/>
        <property name="descripton" column="Descripton"/>
    </class>
</hibernate-mapping>

Note.hbm.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>

    <class name="Notebook.Enities.Note" table="note" schema="notebase">
        <id name="id" type="java.lang.Integer">
            <column name="Id" not-null="true"/>
            <generator class="identity"/>
        </id>
        <one-to-one class="Notebook.Enities.Telephone" name="telephoneId"  cascade="save-update"/>
        <one-to-one class="Notebook.Enities.Description" name="descriptionId" cascade="save-update"/>
        <one-to-one class="Notebook.Enities.Human" name="humanId" cascade="save-update"/>
        <many-to-one class="Notebook.Enities.Group" name="group" fetch="join" lazy="false">
            <column name="Group" not-null="true"/>
        </many-to-one>
    </class>
</hibernate-mapping>


Дампы: Телефон
DROP TABLE IF EXISTS `telephone`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `telephone` (
  `Id` bigint(20) NOT NULL AUTO_INCREMENT,
  `Telephone` varchar(15) NOT NULL,
  `Descripton` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='Human telephone';


Note
CREATE TABLE `note` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `Description_id` int(11) NOT NULL,
  `Human_id` int(20) DEFAULT NULL,
  `Telephone_id` bigint(20) NOT NULL,
  `Group` int(20) DEFAULT NULL,
  PRIMARY KEY (`Id`),
  KEY `fk_human_idx` (`Human_id`),
  KEY `fk_description_idx` (`Description_id`),
  KEY `fk_group_idx` (`Group`),
  KEY `fk_telephone_idx` (`Telephone_id`),
  CONSTRAINT `fk_description` FOREIGN KEY (`Description_id`) REFERENCES `description` (`Id`) ON UPDATE CASCADE,
  CONSTRAINT `fk_group` FOREIGN KEY (`Group`) REFERENCES `group` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `fk_human` FOREIGN KEY (`Human_id`) REFERENCES `human` (`id`) ON UPDATE CASCADE,
  CONSTRAINT `fk_telephone` FOREIGN KEY (`Telephone_id`) REFERENCES `telephone` (`Id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

Файлы немного подрезаны (убран остальной функционал, непричастный).
Пожалуйста, помогите исправить ошибку.
  • Вопрос задан
  • 1723 просмотра
Решения вопроса 1
@bobzer
Java EE Developer
У Вас нигде не определена стратегия генерации первичных ключей, ни на уровне СУБД, ни на уровне вашей бизнес-логики. Для генерации ID могу порекомендовать использовать имеющиеся в MySQL средства. Для этого определите поле первичного ключа в БД как:
ID BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT
а в маппингах сущностей укажите следующее:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
protected Long id;

При таком подходе значение первичного ключа создаётся непосредственно в момент вставки записи в таблицу. При выполнении команды INSERT значение поля ID не указывается, а MySQL сам подставит значение, применив для его генерации сиквенс (специфический объект СУБД, последовательно генерирующий числа, обычно начиная с 1).

Настоятельно не рекомендую использовать примитивы для определения ID в сущности (private int id в Note), т.к. при создании нового экземпляра класса будет присвоено значение по умолчанию 0, что приводит к заблуждению - первичный ключ еще не генерировался, но у него уже есть какое-то значение.

Обычно, класс сущности, описывающий идентификатор, выносится в родительский класс, а все сущности проекта наследуются от него, за счёт чего возникает возможность не описывать поле ID повторно во всех наследниках, избавляясь от дублирования кода. Но, сначала добейтесь работоспособности текущей структуры, а затем занимайтесь рефакторингом иерархии классов. Вот пример определения родительского класса:
import javax.persistence.*;
import java.io.Serializable;

@MappedSuperclass
public abstract class Identifier implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    protected Long id;

    public Long getId() {
        return id;
    }

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

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Identifier that = (Identifier) o;

        if (getId() != null) {
            return getId().equals(that.getId());
        } else {
            return super.equals(o);
        }
    }

    public int hashCode() {
        return getId() != null ? getId().hashCode() : super.hashCode();
    }
}


UPD
Маппинги сущностей БД обычно определяются с помощью аннотаций JPA, следующим образом:
import javax.persistence.*;

@Entity()
@Table(name = "note")
public class Note extends Identifier {
//fields, getters, setters
}
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
Null и 0 это разные вещи.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы