1

I have a REST API which has somebody, now I have a range of number (40000 - 99999) which I have to populate at my end and save it to the DB, I won't be getting it in the body of the request. However I still have to persist this number sequentially

e.g.: 40001, 40002, 40003, etc in Postgres and as it's not an @Id field then I am unable to find a way to persist it in DB, is there any way to do this with Java, JPA?

CREATE SEQUENCE public.certificate
    INCREMENT 1
    START 40000
    MINVALUE 40000
    MAXVALUE 99999999
    CACHE 1;

create table CertificateNumber (c_number integer default nextval(‘certificate’));

@Generated(value = GenerationTime.INSERT)
@Column(name = "c_number", insertable = false,updatable = false)
Integer certificateNumber;

That's the log i am getting

 Resolved [org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [certificate_number]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement]
2020-03-06 16:33:13.167  INFO 864 --- [nio-8080-exec-2] i.StatisticalLoggingSessionEventListener : Session Metrics {
    714213 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    3037131 nanoseconds spent preparing 1 JDBC statements;
    18543560 nanoseconds spent executing 1 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    67929213 nanoseconds spent executing 1 flushes (flushing a total of 1 entities and 0 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}

In response of the API, what i am getting is also mentioned below.

{
    "apierror": {
        "status": "INTERNAL_SERVER_ERROR",
        "timestamp": "06-03-2020 04:33:13",
        "message": "Unexpected error",
        "debugMessage": "could not execute statement; SQL [n/a]; constraint [certificate_number]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement",
        "subErrors": null
    }
}
Maverick
  • 23
  • 5
  • Look at [this](https://stackoverflow.com/questions/60198528) – SternK Mar 05 '20 at 09:54
  • thanks @SternK but will this provide a custom range? I am doubtful about the same – Maverick Mar 05 '20 at 09:59
  • You can create sequence like this `CREATE SEQUENCE my_seq INCREMENT BY 1 MINVALUE 40000 MAXVALUE 99999 START 40000`. – SternK Mar 05 '20 at 10:12
  • Using [@GeneratorType](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#mapping-generated-GeneratorType) you can achieve it even without sequence. – SternK Mar 05 '20 at 10:14
  • Let the database generate and fill that number upon inserting (create an auto-increment field or on insert trigger), then after inserting, refresh the entity to get the number. All the generators in JPA only work for `@Id` annotated fields and not regular fields. Hibernate has some dedicated annotation `@Generated` which might make it work. – M. Deinum Mar 05 '20 at 10:16
  • @SternK GeneratorType anticipates something in parameter, what would it be and from the link, it's not using GeneratorType despite it's using Generated(value = GenerationTime.INSERT) something like this – Maverick Mar 05 '20 at 10:25
  • @Maverick You have two options `@Generated` and `@GeneratorType`. Using `@Generated` assumed that database will generate value during the row insertion. Using `@GeneratorType` assumed that this value will be generated by your implementation of hibernate `ValueGenerator` interface. – SternK Mar 05 '20 at 10:34
  • @Maverick I described it [here](https://stackoverflow.com/a/60216566/6277104) – SternK Mar 05 '20 at 10:38
  • @SternK the second approach seems not to be working from the link that you have shared, I am getting this error could not execute statement; SQL [n/a]; constraint [certificate_number]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement" I am using Postgresql – Maverick Mar 06 '20 at 05:44
  • @Maverick did you try my answer below using trigger? it should works without problems – Bashir Mar 06 '20 at 08:17
  • @Maverick Could you show the relevant code and full stack trace, I'll try to help. – SternK Mar 06 '20 at 08:36
  • @SternK ther error i am getting is this, "apierror": { "status": "INTERNAL_SERVER_ERROR", "timestamp": "06-03-2020 02:21:32", "message": "Unexpected error", "debugMessage": "could not execute statement; SQL [n/a]; constraint [certificate_number]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement", "subErrors": null } – Maverick Mar 06 '20 at 09:14
  • @Generated(value = GenerationTime.INSERT) @Column(name = "c_number", insertable = false,updatable = false) Integer certificateNumber; This is the entity of the Model and when I am triggering the REST API, i am not providing the field certificateNumber, since we have to generate at my end, even though using your approach it's not working, I am using Postgresql – Maverick Mar 06 '20 at 09:18
  • @Maverick Could you please shape this as a new question and add also ddl sql for the column `c_number`. – SternK Mar 06 '20 at 09:23
  • @SternK I have updated the code, please look into it, if there is anything else you need please let me know, and thanks for your prompt replies – Maverick Mar 06 '20 at 09:36
  • @Maverick It looks suspicious that you create `public.certificate` SEQUENCE but as default value use `nextval('certificate')`. Try to change it to `nextval('public.certificate')`. – SternK Mar 06 '20 at 10:15
  • @Maverick helloo .. try using trigger, that will be simpler and it works for me. – Bashir Mar 06 '20 at 10:28
  • @Maverick If it will not help try to enable hibernate sql logs (`hibernate.show_sql`, `hibernate.format_sql`, `hibernate.use_sql_comments`) and find out what sql hibernate can not execute. – SternK Mar 06 '20 at 10:28
  • @Bashir couldn't incorporate the approach with the Java code, if you could provide little more detail with small implementation it would be helpful – Maverick Mar 06 '20 at 10:55
  • @Maverick A database trigger is procedural code that is automatically executed in response to certain events, like in this example will be automatically executed before every insert on your table. It will put a new value in your column using the sequence. If you implement this method, you don't have to edit anything in your java code, your DBMS will handle this action, which will make your application more performer (because the action is done directly in DBMS, no need to pass by your application server) – Bashir Mar 06 '20 at 11:03
  • @SternK I am still getting the same error, I am attaching an image for your reference. I am updating the question once again – Maverick Mar 06 '20 at 11:06

1 Answers1

1

I suggest to use triggers in your Database, first of all you create your sequence:

CREATE SEQUENCE certificateNumber_table_seq
INCREMENT 1
START 1; 

then create this function:

CREATE OR REPLACE FUNCTION trigger_function()
RETURNS trigger AS
$BODY$
BEGIN
New.c_number:=nextval('certificateNumber_table_seq');
Return NEW;
END;
$BODY$

and finally your trigger which will execute the function you created

DROP TRIGGER IF EXISTS trigg_auto_increment ON table_name;
CREATE TRIGGER trigg_auto_increment
BEFORE INSERT
ON table_name //the name of your table
FOR EACH ROW
EXECUTE PROCEDURE trigger_function();
Bashir
  • 2,057
  • 5
  • 19
  • 44
  • it didn't work for me, it's persisting only null value in the db – Maverick Mar 06 '20 at 11:20
  • did you removed @Generated tag? – Bashir Mar 06 '20 at 11:28
  • yes I removed the annotation @ Generated however, I have kept @ Column(name = "c_number", insertable = false,updatable = false) tag, still persisting the same null value :'( – Maverick Mar 06 '20 at 11:35
  • I edited the answer, the last line is `EXECUTE FUNCTION trigger_function();` not `EXECUTE PROCEDURE trigger_function();` and don't forget to change table_name with the name of your table. – Bashir Mar 06 '20 at 11:38
  • and added this line `DROP TRIGGER IF EXISTS trigg_auto_increment ON table_name;` which will remove the old trigger to insert the new one (with the modification I made on the last line) – Bashir Mar 06 '20 at 11:41
  • it's giving me. ERROR: syntax error at or near "FUNCTION" LINE 6: EXECUTE FUNCTION trigger_function(); ^ SQL state: 42601 Character: 154 error, any reason behind it – Maverick Mar 06 '20 at 11:44
  • However when i used procedure instead, it executed successfully but again the same issue, it didn't persist any data in db, i am using postgresql for your reference – Maverick Mar 06 '20 at 11:50
  • well, you have to keep it `PROCEDURE` and try to remove the double quotes (") from at line `RETURNS trigger AS` . But I think that the problem is in `insertable = false,updatable = false` it won't let the trigger update the value – Bashir Mar 06 '20 at 12:07