Ritesh Suvarna
Ritesh Suvarna

Reputation: 57

Hibernate annotation for auto increment but should also save the value if we set it before save

I have a table (PostgreSQL) with 2 auto increment fields, the first one is annotated with @Id and @GeneratedValue, the 2nd one should auto increment and save it, until I set it on purpose.

Is there any way to do this?

@Entity
@Table(name = "route")
public class Route extends BaseEntity {

 private Integer id;
 private Integer routeNumberId;

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 @Column(name = "id")
 public Integer getId() {
    return id;
 }
 ...
 @XmlAttribute
 @Id
 @Basic(optional = false)
 @GeneratedValue(strategy=GenerationType.SEQUENCE,generator="route_number_seq")
 @GenericGenerator(name="route_number_seq",
                   strategy="com.tripms.models.master.UseIdOrGenerate")
 @Column(name = "route_number_id", nullable = false)
 public Integer getRouteNumberId() {
     return routeNumberId;
 }

}

I can't annotate @Id twice and I don't want a composite pk.

Upvotes: 0

Views: 2559

Answers (1)

yntelectual
yntelectual

Reputation: 3228

Mixing auto-increment and manual values is not a good idea. How should your database behave, when your current sequence points to lets say 11, but you insert an entry with value 20? Once the sequence reaches 20, you would get unique constraint violation. In order to prevent it, you would have to verify that the generated sequence value is not in conflict with any existing entry - which is going to introduce whole bunch of other problems with retry, locking etc.

It might be good idea to rethink your model or requirements, why do you the second pseudo Id? And why should it be possible to fill it both manually and automatically? if you insist on this feature, you can get it working, if you guarantee, that the manual entries do not collide with the auto-generated ones. Then you can create a custom ID generator for your ,,real ID" column (Integer id) and override the generate method, so that apart from generating the ID you also check the value of your ,,pseudo Id" (routeNumberId). If it is null, you can ask the DB to give you next value from some sequence. Dummy implementation bellow:

@Override
public synchronized Serializable generate(SessionImplementor session, Object object) throws HibernateException {
    Serializable result;
    if (object instanceof Route ) {
        Route reoute= ((Route ) object);
        if (route.getid() != null) {
            result = (Serializable) route.getId();
        } else {
            result = <GENERATE new ID(e.g. call super, etc)> ;
        }
        if (route.getRouteNumberId() == null) {
            long autoIncrRouteId = <GENERATE new routeID,>
            route.setRouteNumberId(autoIncrRouteId );
        }
        return result;
    } else {
        result = super.generate(session, object);
    }

    return result;
}

Another option is to use @PrePersist entity listener and do the check and/or sequence call there. However it might be tricky to access Entity manager or JPA context from the listeners, as most JPA providers do not fully support injection into entity listeners. You might do a manual jndi lookup, but first rethink what you are trying to achieve if it is worth the trouble.

Edit: functional Id generator code(postgres).

@Override
public Serializable generate(SessionImplementor session, Object obj) throws HibernateException {
    Connection connection = session.connection();
    try {
        Route route= ((Route ) obj);
        String seqName = "you_db_id_sequence";
        PreparedStatement ps = connection.prepareStatement("SELECT nextval ('" + seqName + "') as nextval");
        ResultSet rs = ps.executeQuery();
        if (rs.next()) {
            int id = rs.getInt("nextval");
            log.info("Generated order id {} ", id);
            if (route.getRouteNumberId() == null) {
                route.setrouteNumberid(id); //or call another sequence to get a different id 
            }
            return Long.parseLong(code);
        } else {
           throw new IllegalStateException("could not generate new id");
        }
    } catch (SQLException e) {
        log.error("Could not generate id!", e);
        throw new HibernateException("Unable to generate id from Sequence");
    }
    return null;
}

Upvotes: 1

Related Questions