27

Here is my pojo class

@Entity

public class Department {


@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)

@Column(name="Department_Id")

private Integer deptId;


@Column(name="Department_Name",unique=true,nullable=false)
private String deptName;


@Column(name="Department_Description")
@NotNull
private String deptDesc;
   //geters and setters

What i want is that department_id must be primary key of this Department table and entries for this key must be as DEP0001, DEP0002,DEP0003

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Gaurav Rajput
  • 873
  • 3
  • 11
  • 19
  • What DB you are using? – mrh Jul 01 '15 at 10:19
  • Have a look at this: http://stackoverflow.com/questions/11631800/hibernate-how-specify-custom-sequence-generator-class-name-using-annotations – codependent Jul 01 '15 at 10:32
  • Have a look at this [ http://stackoverflow.com/questions/15731407/how-to-generate-an-alphanumeric-id-in-java] and use custom key generator. – Darshan Jul 02 '15 at 05:45

2 Answers2

39

Thank you everyone for your response...... finally i have done some changes in my Department class and used a class for generating ids........Here is my code

@Entity
public class Department {

@Id
@GenericGenerator(name = "sequence_dep_id", strategy = "com.xyz.ids.DepartmentIdGenerator")
@GeneratedValue(generator = "sequence_dep_id")  
@Column(name="Department_Id")
private String deptId;

@Column(name="Department_Name",unique=true,nullable=false)
private String deptName;


@Column(name="Department_Description")
@NotNull
private String deptDesc;

//getters and setters

DepartmentIdGenerator.java

package com.xyz.ids;

import java.io.Serializable;
import java.sql.*;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.IdentifierGenerator;

public class DepartmentIdGenerator implements IdentifierGenerator{

    @Override
    public Serializable generate(SessionImplementor session, Object object)
            throws HibernateException {

        String prefix = "DEP";
        Connection connection = session.connection();

        try {
            Statement statement=connection.createStatement();

            ResultSet rs=statement.executeQuery("select count(Department_Id) as Id from demo.Department");

            if(rs.next())
            {
                int id=rs.getInt(1)+101;
                String generatedId = prefix + new Integer(id).toString();
                System.out.println("Generated Id: " + generatedId);
                return generatedId;
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


        return null;
    }

}
Dennis van Opstal
  • 1,294
  • 1
  • 22
  • 44
Gaurav Rajput
  • 873
  • 3
  • 11
  • 19
  • 1
    eclipse complains about it saying no generator named is defined in persistence unit – erickdeoliveiraleal Apr 18 '17 at 15:59
  • did you also defined it in persistence.xml or it is bug in eclipse? – erickdeoliveiraleal Apr 18 '17 at 16:00
  • 8
    The lack of this is when we delete one entry from the table and try to create new one. we can have a primary key constraint violation ! – sic-sic Sep 14 '17 at 19:18
  • @sic-sic you are absolutely right. do you have any idea how to do it correctly. – Gaurav Rajput Sep 16 '17 at 09:24
  • 8
    You can use a "select max(Department_id)" instead of "select count(Departement_id)" if Department_id is a number type – sic-sic Sep 18 '17 at 11:04
  • the constraint-exception occurs when we have IDs 1, 2, 3 and then delete 1. Count will return 2 and IDs 2 and 3 are already occupied. You MUST use the select MAX variant. – SCI Aug 29 '18 at 09:24
  • You can create `hibernate_sequence` table where autoincremented values will be stored. Look here: http://myjourneyonjava.blogspot.com/2015/01/creating-custom-generator-class-in.html – nikiforovpizza Oct 24 '19 at 15:10
  • In Spring Boot 2.7 with Hibernate 5.6.9 you have to use `SharedSessionContractImplementor` instead of `SessionImplementor` – swissbuechi Jun 22 '22 at 07:48
4

The best way to implement custom id generator in Hibernate.

@Entity
@Table(name = "employee_customid")
public class Employee implements Serializable {
@Id
@GenericGenerator(name = "string_based_custom_sequence", strategy = 
"com.ie.customid.EmployeeIdGenerator")
@GeneratedValue(generator = "string_based_custom_sequence")
@Column(name = "custom_emp_id")
private String id;

@Column(name = "emp_name")
private String name;
@Column(name = "emp_age")
private Integer age;
@Column(name = "emp_salary")
private Double salary;
// getter setter and toString

Below code is implementation for generating custom id in hibernate with any database code will be same (with minimal changes)

package com.ie.customid;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.IdentifierGenerator;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class EmployeeIdGenerator implements IdentifierGenerator {
    //You can give any name to sequence be sure that you know how to use it.
    private final String DEFAULT_SEQUENCE_NAME = "hibernate_sequence";
    //private final String DEFAULT_SEQUENCE_NAME = "hib_sequence";

    /*
    * This method will generate custom id based on String followed by id
    * e.g. emp0001, emp0002, emp0003 and so on..
    * */
    public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
        Serializable result = null;
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        String prefix = "emp";
        try {
            connection = session.connection();
            statement = connection.createStatement();
            try {
                /*
                * uncomment below line if you are using mysql and the sequence DOES NOT EXIST.
                * As we all know MySql does not support sequence, instead there is AUTO INCREMENT
                * if you are using other databases change SQL according to that
                * e.g. Oracle: "SELECT "+sequenceName+".NEXTVAL FROM DUAL"
                * PostgreSQL: "SELECT  NEXTVAL('+sequenceName+"')  
                * */
                //statement.executeUpdate("UPDATE " + DEFAULT_SEQUENCE_NAME + " SET next_val=LAST_INSERT_ID(next_val+1)");
                resultSet = statement.executeQuery("SELECT next_val FROM  " + DEFAULT_SEQUENCE_NAME);
            } catch (Exception e) {

                System.out.println("In catch, cause : Table is not available.");
                // if sequence is not found then creating the sequence
                // Below code is for MySql database you change according to your database
                statement.execute("CREATE table " + DEFAULT_SEQUENCE_NAME + " (next_val INT NOT NULL)");
                statement.executeUpdate("INSERT INTO " + DEFAULT_SEQUENCE_NAME + " VALUES(0)");
                //==> LAST_INSERT_ID(next_val+1)  -> this is inbuilt function of MySql so by using this we can achieve our custom sequence like auto increment
                statement.executeUpdate("UPDATE " + DEFAULT_SEQUENCE_NAME + " SET next_val=LAST_INSERT_ID(next_val+1)");
                resultSet = statement.executeQuery("SELECT next_val FROM  " + DEFAULT_SEQUENCE_NAME);
                //e.printStackTrace();
            }
            if (resultSet.next()) {

                int nextValue = resultSet.getInt(1);
                String suffix = String.format("%04d", nextValue);
                result = prefix.concat(suffix);
                System.out.println("Custom generated sequence is : " + result);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return result;
    }
}

If you like to use XML configuration then use below code

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hbm2ddl.auto">update</property>
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/Testing</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <mapping class="com.ie.entity.Employee"/>
        <!-- uncomment below line if you want to use XML based mapping & NOTE :  if you will use both then XML will override annotation  -->
<!--        <mapping resource="Employee.hbm.xml"></mapping>-->
    </session-factory>
</hibernate-configuration>

Below is Employee mapping file

<?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="com.ie.entity.Employee" table="employees_customid">
        <id name="id" column="employeeId">
            <generator class="com.ie.customid.EmployeeIdGenerator"/>
        </id>
        <property name="name" column="e_name"></property>
        <property name="age" column="e_age"/>
        <property name="salary" column="e_salary"/>
    </class>
</hibernate-mapping>
  • Incase anyone is doing the method suggested by Sumit , then you must remember to rollback the transaction in the inner catch block with `connection.rollback()` . The default behavior of PostgreSQL is to prevent a user from going forward with commits after even a single statement fails, so to bypass that, this statement is necessary. – need_to_know_now Feb 23 '21 at 22:09