javahibernatejpa

NULL not allowed for column 'ID' in Hibernate


I am creating integrational tests for my dao objects. I am not allowed to use Spring framework. This is the model:

import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "pets")
@Data
public class Pet {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Setter(AccessLevel.NONE)
    private Long id;

    @Column
    private String name;

    @Column
    @Enumerated(EnumType.STRING)
    private Breed breed;

    @Column
    @Enumerated(EnumType.STRING)
    private FurColor color;

    @ManyToOne
    @JoinColumn(name = "owner_id")
    private Owner owner;

    @ManyToMany
    @JoinTable(
            name = "pet_friends",
            joinColumns = @JoinColumn(name = "pet_id"),
            inverseJoinColumns = @JoinColumn(name = "friend_id")
    )
    @Setter(AccessLevel.NONE)
    private List<Pet> friends = new ArrayList<>();

    public void AddFriend(Pet friend) {
        friends.add(friend);
    }
}

I am trying to do the basic tests using JUnit 5 and DbUnit built with maven These are the pom.xml dependencies

<dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.5.7.Final</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.36</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>2.2.224</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.dbunit</groupId>
            <artifactId>dbunit</artifactId>
            <version>2.7.3</version>
            <scope>test</scope>
        </dependency>
 
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.9.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

This the dao save method:

@Override
    public Pet save(Pet entity) {
        var session = hib.openSession();
        var transaction = session.beginTransaction();

        try {
            session.save(entity);
            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
        }

        session.close();
        return entity;
    }

This is the dao test:

@TestInstance(TestInstance.Lifecycle.PER_METHOD)
public class PetDaoTests {
    private SessionFactory sessionFactory;
    private PetDao petDao;
    private DataSource dataSource;

    @BeforeEach
    public void setUp() {
        var config = new Configuration().configure("test.hibernate.cfg.xml");
        sessionFactory = config.buildSessionFactory();
        petDao = new PetDao(sessionFactory);

        var jdbch2 = new JdbcDataSource();
        jdbch2.setUrl(config.getProperty("hibernate.connection.url"));
        jdbch2.setUser(config.getProperty("hibernate.connection.username"));
        jdbch2.setPassword(config.getProperty("hibernate.connection.password"));
        dataSource = jdbch2;
    }

    @AfterEach
    public void tearDown() {
        sessionFactory.close();
    }

    @Test
    public void checlconn() {
        var p = new Pet();
        p.setName("some");
        petDao.save(p);
        System.out.println(p);

        var session = sessionFactory.openSession();
        var pets = session.createQuery("from Pet", Pet.class).list();
        Assertions.assertEquals(1, pets.size());
    }
}

Hibernate configuration:

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>

        <property name="hibernate.connection.driver_class">org.h2.Driver</property>
        <property name="hibernate.connection.url">jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1</property>
        <property name="hibernate.connection.username">sa</property>
        <property name="hibernate.connection.password">sa</property>

        <property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
        <property name="hbm2ddl.auto">create</property>
        <property name="hibernate.current_session_context_class">thread</property>

        <mapping class="ru.serov.model.Pet" />
        <mapping class="ru.serov.model.Owner" />

    </session-factory>
</hibernate-configuration>

I know that using all the instruments of DbUnit with JUnit 5 may not work that's why I implement all the methods natively

The error it returns is:

ERROR: Значение NULL не разрешено для поля "ID"
NULL not allowed for column "ID"; SQL statement:
insert into pets (id, breed, color, name, owner_id) values (null, ?, ?, ?, ?) [23502-224]

right after insertion - but as I think the committing of transaction must be flushing the session and the changes must be visible

I also thought that there is something about hibernate.current_session_class property but nothing changed when I edit it

There were some attempts to create the same thing on JUnit 4 with DbUnit but had the same error

what is the cause of the problem?


Solution

  • This error means that Hibernate is trying to insert null to id column. But id should be auto-generated because you use @GeneratedValue(strategy = GenerationType.IDENTITY).

    To fix this, you can try to add the following setting in your hibernate.cfg.xml file:

    <property name="hibernate.id.new_generator_mappings">true</property>
    

    This tells Hibernate to use new ID generation system, which works better with databases like H2.

    From official Hibernate docs (section: 23.5.2, Identifier options):

    hibernate.id.new_generator_mappings (e.g. true (default value) or false) Setting which indicates whether or not the new IdentifierGenerator are used for AUTO, TABLE and SEQUENCE.

    Existing applications may want to disable this (set it false) for upgrade compatibility from 3.x and 4.x to 5.x.