Can you use Postgres SERIAL for generating PK in hibernate/JPA?

Please don't tell me that JPA has ruled out using auto-incrementing or autogenerating functionalities of individual databases as a standard method for generating primary keys for new objects.

I am reading the Java EE Tutorial and have finally come across a section which has brought me great disappoinment:

In VendorPart, the following code specifies the settings for generating primary

key values:

@TableGenerator(

name="vendorPartGen",

table="EJB_ORDER_SEQUENCE_GENERATOR",

pkColumnName="GEN_KEY",

valueColumnName="GEN_VALUE",

pkColumnValue="VENDOR_PART_ID",

allocationSize=10)

@Id

@GeneratedValue(strategy=GenerationType.TABLE,

generator="vendorPartGen")

public Long getVendorPartNumber(){

return vendorPartNumber;

}

The @TableGenerator annotation is used in conjunction with @Generated-

Value抯 strategy=TABLE element. That is, the strategy used to generate the pri-

mary keys is use a table in the database.

What ever happened to leveraging the auto-incrementing or sequencing functionality of the various different databases? Does this mean I have to code all of this for each and every single table that contains a primary key? OUCH! I was doing so good with hibernate on SJSAS 9.x using Netbeans 5.5 until I run into this. Everything was flowing so easily and then out of nowhere, BAM! Complexity seemingly out of nowhere only to prove sudden inconsistency with the rest of the very nice and easy pathway (right-click->persistence->Create Entity beans from db, ->Create Session beans for entity beans, call EJB, etc). It's a shame to have to leave JPA, undoing everything only to go back to a more straight forward (but simple and effective), yet lengthy JDBC approach.

Please, tell me it isn't so, and that I am over-reacting and that I simply have not yet found the standard documentation to achieve this very thing without having to maintain primary keys in separate tables. Sure I can see how vendor independant this can prove to be, but holy! Why not let the JPA implementations deal with "how" the primary keys are generated (using tables as last resorts, otherwise leveraging whatever functionality/features each db has to offer) and have all of this transparent to the developer who simply wants to store a new object?

[2668 byte] By [javiousa] at [2007-10-3 8:04:28]
# 1

ha HAAA! I think I've managed to eliminate my own fear!

I have managed to find the code snippet from a third party web site implementing JPA:

@Id

@GeneratedValue(generate=GenerationType.IDENTITY)

private long id;

Whew!! Hopefully this will work when I get home to try it out and I can put my JDBC guns away.

Thanks me! (reach..., pat, pat).

javiousa at 2007-7-15 3:08:09 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 2

[nobr]OK, cool!

Well since nobody here seems to have the answer for this, I have opted to do sacrifice my "free(but not really)" time to investigate and research this myself, filling in the blanks where necessary (essentially trial and error).

This leaves me with great news!

Yes, this DOES mean yet another table for generating IDs, however, it is at least only one single table which can potentially house many ids for all primary keys required for all tables that require them. This does not mean you cannot specify a separate table for each primary key if you want to. You can do this, or even specify a separate table for a group of primary keys for specific sets of tables. I makes sense to me to keep things as simple as possible and simply have one table to hold all primary keys, unless of course you want a certain set of tables to have Longs as primary keys and another to have Integers as primary keys and so on.

Anyhow, without further adue, here is what I had to do:

Within my entity class, I simply located the area where the initial Id was generated by NetBeans and modified it as follows:

@TableGenerator(

name="nickGen",

table="EJB_NICK_GENERATOR",

pkColumnName="GEN_KEY",

valueColumnName="GEN_VALUE",

pkColumnValue="NICK_ID",

allocationSize=10)

@Id

@GeneratedValue(strategy=GenerationType.TABLE,

generator="nickGen")

@Column(name = "test_id", nullable = false)

private Integer testId;

Note, that the table name for my intentions, should really be called something like "EJB_PRIMARY_KEYS" instead of being specific to "NICK"name. Then, I did have to go and create the table:

create table EJB_NICK_ID_GENERATOR(

GEN_KEY VARCHAR(20) NOT NULL,

GEN_VALUE INTEGER NOT NULL

)

But one delightful thing I hadn't anticipated was that, this was all I had to do. I thought I had to insert a row into this table in order to match up the columns to the generator annotation, but nope! The compilation and deployment creates this entry for me if it doesn't already exist! Cool!

I then created a jsp:

<%@page contentType="text/html"%>

<%@page pageEncoding="UTF-8"%>

<%--

The taglib directive below imports the JSTL library. If you uncomment it,

you must also add the JSTL library to the project. The Add Library... action

on Libraries node in Projects view can be used to add the JSTL 1.1 library.

--%>

<%--

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

--%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>JSP Page</title>

</head>

<body>

<h1>JSP Page</h1>

<form action="TestIncrement" method="post">

NickName<input type="text" name="nickname"><br>

<input type="submit" value="Submit">

</form>

<%--

This example uses JSTL, uncomment the taglib directive above.

To test, display the page like this: index.jsp?sayHello=true&name=Murphy

--%>

<%--

<c:if test="${param.sayHello}">

<!-- Let's welcome the user ${param.name} -->

Hello ${param.name}!

</c:if>

--%>

</body>

</html>

and then a servlet to post to, which would in turn insert a new nickname:

/*

* TestIncrement.java

*

* Created on October 25, 2006, 10:18 AM

*/

package sample4;

import java.io.*;

import java.net.*;

import javax.ejb.EJB;

import javax.servlet.*;

import javax.servlet.http.*;

import sample5.Testinc;

import sample5.TestincFacadeLocal;

/**

*

* @author javious

* @version

*/

public class TestIncrement extends HttpServlet {

@EJB

private TestincFacadeLocal testincFacade;

/** Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.

* @param request servlet request

* @param response servlet response

*/

protected void processRequest(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

String nickname=request.getParameter("nickname");

Testinc testInc = new Testinc();

testInc.setNickname(nickname);

testincFacade.create(testInc);

request.getRequestDispatcher("test_inc_entry.jsp").forward(request,response);

}

// <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">

/** Handles the HTTP <code>GET</code> method.

* @param request servlet request

* @param response servlet response

*/

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

processRequest(request, response);

}

/** Handles the HTTP <code>POST</code> method.

* @param request servlet request

* @param response servlet response

*/

protected void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

processRequest(request, response);

}

/** Returns a short description of the servlet.

*/

public String getServletInfo() {

return "Short description";

}

// </editor-fold>

}

and presto! It works! Cool. *Whew!* Thanks again, ME!!!

I love getting so much help from this forum. :p[/nobr]

javiousa at 2007-7-15 3:08:10 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 3

That's not really using the SERIAL from postgres. To use the database's serial type you only need to add the line.

@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="testtable_testid_seq")

It will auto increment and the new value will be available with the getTestid() method after you commit.

Jaxena at 2007-7-15 3:08:10 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 4

Well thank you VERY much, Jaxen,

While although the solution you suggested did not actually work for me, it did enable to re-open this case with a new view on things and I have now managed to get it to work using the following TWO lines of code:

@SequenceGenerator(name="genCommentId", sequenceName="guest_comment_comment_id_seq")

@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="genCommentId")

once again, thank you so much! I've been wishing I could do it this way, but couldn't figure out how. Now I just need to go through all my code and do the updates to the new and improved way.

javiousa at 2007-7-15 3:08:10 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...