ORA_02201 Sequence is not allowed here - java

hope you all doing well. I'm continuously getting "ORA_02201 - Sequence is not allowed here" SQL syntax error when I'm trying to post new entry to Database using JSON. I'm using Oracle Autonomous Database
In Oracle Database, folks use GenerationType.SEQUENCE strategy for ID (Primary Key) increment.
I've tried something beforehand, let me share with you. It seems root of the issue is that Sequence is not working with JSON, or not set up to be working. It gives "Sequence is not allowed here" when my JSON request doesn't have id, so when Sequence is supposed to work.
Note that, I have this app as MVC web application where I save entity into database through getting data from form input in jsp pages. It works flawlessly and generate ID correctly. Problem is with JSON I believe.
{
"firstName" : "Mark",
"lastName" : "Heiberg",
"email" : "heibergmark#dot.com"
}
I made request by specifying the Id in JSON which id exists in database, then it updated the entry. So I get assured that root of the cause is id generation, when it should make new id, it doesn't work. I couldn't find anything about JSON-Oracle DB relationship in Internet. I wanted to ask here, maybe you know can I or should I do something to enable support for Sequence generation type on the moment of JSON request. Thanks in advance.
#PostMapping("/customers")
Customer addCustomer(#RequestBody Customer customer){
customer.setId(0); //set id to 0 to force it to add customer to DB, instead of updating current.
customerService.saveCustomer(customer);
return customer;
}
This is Table Definition SQL: (I've built it using Oracle APEX environtment, don't mind that 9999999999999999, it's by default)
CREATE TABLE "CUSTOMER"
( "ID" NUMBER(3,0) GENERATED BY DEFAULT AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE NOKEEP NOSCALE NOT NULL ENABLE,
"FIRST_NAME" VARCHAR2(15) COLLATE "USING_NLS_COMP",
"LAST_NAME" VARCHAR2(15) COLLATE "USING_NLS_COMP",
"EMAIL" VARCHAR2(30) COLLATE "USING_NLS_COMP",
CONSTRAINT "CUSTOMER_PK" PRIMARY KEY ("ID")
USING INDEX ENABLE
) DEFAULT COLLATION "USING_NLS_COMP"
/
CREATE OR REPLACE EDITIONABLE TRIGGER "BI_CUSTOMER"
before insert on "CUSTOMER"
for each row
begin
if :NEW."ID" is null then
select "CUSTOMER_SEQ".nextval into :NEW."ID" from sys.dual;
end if;
end;
/
ALTER TRIGGER "BI_CUSTOMER" ENABLE
/
This is my Entity class where Database annotations is implemented:
package com.customer_tracker.entity;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
#Entity
#Table(name = "CUSTOMER")
public class Customer {
public Customer() {
}
public Customer(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Customer_SEQ")
#SequenceGenerator(name = "Customer_SEQ", sequenceName = "CUSTOMER_SEQ", allocationSize = 1)
#Column(name = "ID")
private int id;
#NotNull(message = "This field is required!")
#Size(min = 3, max = 15, message = "Write something!")
#Column(name = "FIRST_NAME")
private String firstName;
#NotNull(message = "This field is required!")
#Size(min = 3, max = 15, message = "Write something!")
#Column(name = "LAST_NAME")
private String lastName;
#NotNull(message = "This field is required!")
#Size(min = 3, max = 30, message = "Write something!")
#Column(name = "EMAIL")
private String email;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName.trim().replaceAll("\\s", "");
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName.trim().replaceAll("\\s", "");
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email.trim().replaceAll("\\s", "");
}
#Override
public String toString() {
return "id = " + id +
", firstName = " + firstName + ", lastName = " + lastName + ", email = " + email;
}
}
This is the code snippet where Hibernate going to save entry to Database:
package com.customer_tracker.dao;
import com.customer_tracker.entity.Customer;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public class CustomerList implements CustomerDAO{
#Autowired
private SessionFactory sessionFactory;
#Override
public void saveCustomer(Customer customer) {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(customer);
}
}
... GET, PUT, DELETE methods work as desired, only POST doesn't work as database should generate id behind the scenes.

The problem is that you are using an identity column with the option generated always, but at the same time you are using a trigger to populate the column ID with a sequence. You can't do that, either you use a sequence and a trigger, or you use identity columns. If you are in Oracle 12c or higher, I strongly recommend you to use identity columns, unless some of the restrictions they inherit may affect the way your application behaves.
See this example
SQL> create table test_col ( c1 number generated always as identity start with 1 increment by 1 , c2 varchar2(1) ) ;
Table created.
SQL> insert into test_col ( c2 ) values ( 'A' ) ;
1 row created.
SQL> select * from test_col ;
C1 C
---------- -
1 A
SQL> insert into test_col ( c1,c2 ) values ( 2,'B' ) ;
insert into test_col ( c1,c2 ) values ( 2,'B' )
*
ERROR at line 1:
ORA-32795: cannot insert into a generated always identity column
You have to drop the trigger and the sequence as they are useless in this scenario. The error is surely raised when the trigger is fired. When you use identity columns, Oracle handles automatically for you the sequence and the insert.
The identity columns are subject to the following restrictions:
Each table has one and only one identity column.
The data type of the identity column must be a numeric data type. the user-defined data type is not allowed to use with the identity clause.
The identity column is not inherited by the CREATE TABLE AS SELECTstatement.
The identity column cannot have another DEFAULT constraint.
The encryption algorithm for encrypted identity columns can be inferred therefore you should use a strong encryption algorithm.
The inline constraint of the identity column must not conflict with the NOT NULL and NOT DEFERRABLE constraint stated by the identity clause.
generated always is one of the options for Identity Columns. Read more about that in the link below
Identity Columns

if you are using JPA you should have #Id and #GeneratedValue (with proper strategy) annotations over id field and it will be auto-generated.
Example: https://spring.io/guides/gs/accessing-data-jpa/

Related

DataException: could not execute query

I get this error when I try to run start my application:
org.springframework.dao.DataIntegrityViolationException: could not execute query; SQL [SELECT * FROM testquestions ORDER by id DESC LIMIT 1]; nested exception is org.hibernate.exception.DataException: could not execute query
As seen in previous problems on StackOverflow, I tried to adjust the length of my data input in my sql file and I've set the length of my #Column to the same amount of characters. this didn't help.
this is my #Table class:
#Entity
#Getter
#Setter
#Table(name = "testquestions")
public class TestQuestion {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "questiontitle", length = 2000)
private String questionTitle;
#Column(name = "info", length = 4096)
private String Info;
#Column(name = "solvetime")
private int solveTime;
#Column(name = "difficultylevel")
private DifficultyLevel difficultyLevel;
#Column(name = "questionimage")
private Image questionImage;
public TestQuestion(){
}
public TestQuestion(int id, String questionTitle, String info, DifficultyLevel difficultyLevel) {
this.id = id;
this.questionTitle = questionTitle;
Info = info;
this.difficultyLevel = difficultyLevel;
}
public String getInfo() {
return Info;
}
}
This is my # Query in my QuestionRepository:
#Query(value = "SELECT * FROM testquestions ORDER by id DESC LIMIT 1", nativeQuery = true)
TestQuestion fetchLastQuestion();
This is my database.sql file, it writes to a PostgreSQL data base:
TRUNCATE TABLE users CASCADE;
TRUNCATE TABLE testquestions CASCADE;
DROP TABLE users;
DROP TABLE testquestions;
CREATE TABLE users(
id int,
username varchar(255),
password varchar(255),
role varchar(255)
);
CREATE TABLE testquestions(
id int primary key ,
questiontitle varchar(2000),
info varchar(4096),
solvetime int,
difficultylevel varchar(255),
questionimage bytea
);
INSERT INTO users(id, username, password, role)
VALUES (0, 'user', 'u', 'user'),
(1, 'user','u','user');
INSERT INTO testquestions(id,questiontitle, info, solvetime, difficultylevel, questionimage)
VALUES (0, 'Multiple Databases', 'A company wants to use Spring Boot in a web application which should use JPA as a database abstraction. The unit tests should be run on an H2 database while the production should run on a MySQL database.
Select all the things that need to be done or that will be done automatically by Spring Boot.', 3, 'Easy',
''),
(1, 'Screen Orientation', 'Which of these methods are called when the screen changes orientation from portrait to landscape in Android?',
3, 'Easy',''),
(2, 'Merge Names', 'Implement the uniqueNames method. When passed two arrays of names, it will return an array containing the names that appear in either or both arrays. The returned array should have no duplicates.
For example, calling MergeNames.uniqueNames(new String[]{''Ava'', ''Emma'', ''Olivia''}, new String[]{''Olivia'', ''Sophia'', ''Emma''}) should return an array containing Ava, Emma, Olivia, and Sophia in any order.',
10, 'Easy',''),
(3, 'Date', 'Write a function that converts user entered date formatted as M/D/YYYY to a format required by an API (YYYYMMDD). The parameter "userDate" and the return value are strings.
For example, it should convert user entered date "12/31/2014" to "20141231" suitable for the API.', 10, 'Easy', ''),
(4, 'Inspector', 'Fix the bugs in the following HTML code.', 10, 'Easy',''),
(5, 'Train Composition', 'A TrainComposition is built by attaching and detaching wagons from the left and the right sides, efficiently with respect to time used.
For example, if we start by attaching wagon 7 from the left followed by attaching wagon 13, again from the left, we get a composition of two wagons (13 and 7 from left to right). Now the first wagon that can be detached from the right is 7 and the first that can be detached from the left is 13.
Implement a TrainComposition that models this problem.', 20, 'Hard', '');
Has anyone got an idea how to fix this error?
Thanks!
Tom

How to fix error in SimpleJdbcInsert in SpringBoot application

I learn Spring Boot reading book Spring in Action 5. I try to insert data to H2 embedded database.I use SimpleJdbcInsert and executeAndReturnKey method.
Here is a Constructor:
#Autowired
public JdbcOrderRepository(JdbcTemplate jdbc) {
this.orderInserter = new SimpleJdbcInsert(jdbc).withTableName("Taco_Order").usingGeneratedKeyColumns("id");
this.orderTacoInserter = new SimpleJdbcInsert(jdbc).withTableName("Taco_Order_Tacos");
this.objectMapper = new ObjectMapper();
}
Here is methods to insert data:
#Override
public Order save(Order order) {
order.setPlacedAt(new Date());
long orderId = saveOrderDetails(order);
order.setId(orderId);
List<Taco> tacos = order.getTacos();
for (Taco taco : tacos)
saveTacoToOrder(taco, orderId);
return order;
}
private long saveOrderDetails(Order order) {
Map<String, Object> values = objectMapper.convertValue(order, Map.class);
values.put("placedAt", order.getPlacedAt());
long orderId = orderInserter.executeAndReturnKey(values).longValue();
return orderId;
}
The error is on this string:
long orderId = orderInserter.executeAndReturnKey(values).longValue();
Error text:
There was an unexpected error (type=Internal Server Error, status=500).
PreparedStatementCallback; ???????? NULL ?? ????????? ??? ????
"DELIVERYNAME" NULL not allowed for column "DELIVERYNAME"; SQL statement:
INSERT INTO Taco_Order (DELIVERYNAME, DELIVERYSTREET, DELIVERYCITY,
DELIVERYSTATE, DELIVERYZIP, CCNUMBER, CCEXPIRATION, CCCVV, PLACEDAT)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) [23502-197]; nested exception is
org.h2.jdbc.JdbcSQLException: ???????? NULL ?? ????????? ??? ????
"DELIVERYNAME" NULL not allowed for column "DELIVERYNAME"; SQL statement:
INSERT INTO Taco_Order (DELIVERYNAME, DELIVERYSTREET, DELIVERYCITY,
DELIVERYSTATE, DELIVERYZIP, CCNUMBER, CCEXPIRATION, CCCVV, PLACEDAT)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) [23502-197]
Bur in debugger evrything is alright:
value parameter is fully loaded by values.
idea screensot
Class Order:
#Data
public class Order {
#NotBlank(message = "Name is required")
private String name;
#NotBlank(message = "Name is required")
private String street;
#NotBlank(message = "Name is required")
private String city;
#NotBlank(message = "Name is required")
private String state;
#NotBlank(message = "Name is required")
private String zip;
#CreditCardNumber(message = "Not valid cc")
private String ccNumber;
#Pattern(regexp = "^(0[1-9]|1[0-2])([\\/])([1-9][0-9])$", message = "Must be formatted MM/YY")
private String ccExpiration;
#Digits(integer = 3, fraction = 0, message = "Invalid CVV")
private String ccCVV;
private Long id;
private Date placedAt;
List<Taco> tacos = new ArrayList<>();
public void addDesign(Taco design) {
this.tacos.add(design);
}
}
How to fix this problem and insert data to H2 database using SimpleJdbcInsert?
I faced same issue today. I was able to resolve this.
To fix this remove, go to schema.sql(file in which you are creating tables) and remove delivery from fields in Taco_Order table.
From book
SimpleJdbcInsert has a couple of useful methods for executing the insert: execute() and executeAndReturnKey(). Both accept a Map, where the map keys correspond to the column names in the table the data is inserted into. The map values are inserted into those columns.
Keys name are fields from Order.java
I went through the screen shot and i do not see any value for column DELIVERYNAME.
The issue is because of the table in which you are running the insert operation doesn't allow null values for the column DELIVERYNAME.
Ways to solve this:-
Remove the NOT NULL constraints from the column in the table definition.
Remove the #NotBlank annotation from the members of the object if you are not sure whether the data is going to be blank or not.
Use proper mapping between the column names in table and those with the member name in Class/Entity Definition. You can use #Column annotation to easily do that.
As much I can understand following your code and screenshot, that the issue is because of improper mapping between column names and that of the member of the object.
It is recommended that rather than relying on the auto mapping of Spring Boot coder should manually do the mapping using #Column annotation or mapping through hibernate configuration file.
The issue is with attributes name in Order class. These should mirror the coloumn name of respective table.
Eg:
private String deliveryName; -> would be mapped to deliveryName field in db.
The issue has arisen as in schema.sql the field is named as "deliveryName" while in ingredient class the attirbute is named "name".
This answer is for those who follow the examples from book "Spring in action". You won't do such thing in real life, but if you're a reader of this book it is reasonable to replace this:
Map<String, Object> values = objectMapper.convertValue(order, Map.class);
values.put("placedAt", order.getPlacedAt());
with this:
Map<String,Object> values = new HashMap<>();
values.put("ID", order.getId());
values.put("PLACEDAT", order.getPlacedAt());
values.put("DELIVERYNAME", order.getName());
values.put("DELIVERYSTREET", order.getStreet());
values.put("DELIVERYCITY", order.getCity());
values.put("DELIVERYSTATE", order.getState());
values.put("DELIVERYZIP", order.getZip());
values.put("CCNUMBER", order.getCcNumber());
values.put("CCEXPIRATION", order.getCcExpiration());
values.put("CCCVV", order.getCcCVV());
Previous answers mentioned changing schema which will cause problems if you will follow examples from next chapters or using jpa annotations to perform mapping, but according to the author of the book you don't know about them because they are introduced in the next chapters. Besides you won't use JdbcOrderRepository class from the next chapter.
Names of fields in Order class must be the same as columns names in table Taco_Order.
#Data
public class Order {
private Long id;
private Date placedAt;
#NotBlank(message = "Podanie imienia i nazwiska jest obowiazkowe")
private String deliveryName;
#NotBlank(message = "Podanie ulicy jest obowiązkowe")
private String deliveryStreet;
#NotBlank(message = "Podanie miasta jest obowiązkowe")
private String deliveryCity;
#NotBlank(message = "Podanie województwa jest obowiązkowe")
private String deliveryState;
#NotBlank(message = "Podanie kodu pocztowego jest obowiązkowe")
private String deliveryZip;
#CreditCardNumber(message = "To nie jest prawidłowy numer karty kredytowej")
private String ccNumber;
#Pattern(regexp = "^(0[1-9]|1[0-2])([\\/])([1-9][0-9])$", message = "Wartość musi być w formacie MM/RR")
private String ccExpiration;
#Digits(integer = 3, fraction = 0, message = "Nieprawidłowy kod CVV")
private String ccCVV;
public List<Taco> tacos = new ArrayList<>();
public void addDesign(Taco design) {
this.tacos.add(design);
}
public List<Taco> getTacos() {
return tacos;
}
}
I encountered the same problem today and all I had forgotten to do was to update the fields in the order.html file to deliveryName, deliveryCity etc.

Native named query fails with exception "column is of type date but expression is of type bytea"when NULL LocalDate is given as input

Query:
INSERT INTO PERSON
(email, mobile, party_id, affiliate_id, eligibility, member_start_date, created_by, created_dt, first_name, last_name, google_connected)
values
('xxx#yyy.org', NULL, 123, '123', '1', NULL, NULL, '2018-8-30 21:45:56.859000 -6:0:0', 'xxx', 'yyy', '0')
ON CONFLICT (email)
DO UPDATE SET create_dt = '2018-8-30 21:45:56.859000 -6:0:0' where email = ?
When the LocalDate value is not null, it works fine. Facing this issue only when LocalDate value is given as null.
Even after PostgreSQL casting, it does the same.
Exception stacktrace:
2018-08-30 21:10:48,372 -- [ERROR]-- There was an unexpected problem
with your request org.postgresql.util.PSQLException: ERROR: column
"member_start_date" is of type date but expression is of type bytea
Hint: You will need to rewrite or cast the expression. Position: 185
at
org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2182)
at
org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1911)
at
org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:173)
at
org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:645)
at
org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:495)
at
org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:380) at sun.reflect.GeneratedMethodAccessor98.invoke(Unknown Source) at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498) at
org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:114)
at com.sun.proxy.$Proxy185.executeQuery(Unknown Source) at at
org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:70)
... 149 common frames omitted
Entity:
#Entity(name = "person")
#EqualsAndHashCode(callSuper = false)
public class PersonEntity extends Audit {
#Id
#GeneratedValue
#Column(name = "person_id", columnDefinition = "uuid", updatable = false)
private UUID id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
#NotNull
private String email;
#Column(name = "mobile")
private String mobile;
#Column(name = "party_id")
private Long partyId;
#Column(name = "affiliate_id")
private String affiliateId;
#Column(name = "eligibility")
#NotNull
private Boolean eligibility;
#Column(name = "member_start_date")
private LocalDate memberStartDate;
#Column(name = "google_connected")
private Boolean googleAccountConnected;
}
PostgreSQL table definition; it's missing google_connected column which is not important:
CREATE TABLE person
(
person_id UUID NOT NULL,
email VARCHAR(128) NOT NULL,
mobile VARCHAR(20),
party_id INTEGER,
affiliate_id VARCHAR(20),
eligibility BOOLEAN NOT NULL,
member_start_date DATE,
created_by VARCHAR(128) NOT NULL,
created_dt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by VARCHAR(128) DEFAULT NULL,
updated_dt TIMESTAMP NULL,
CONSTRAINT person_pk PRIMARY KEY ( person_id )
);
Because the query is native, Hibernate doesn't know the data types which to expect, so when you pass a null it defaults to the generic Serializable type handler. Changing this behaviour breaks compatibility with other databases.
Postgres, however parses the query immediately and determines what types are acceptable, and it always checks for type before it checks for null. They are the only ones who can fix this, but refuse to do so and say it works as intended.
The only solutions for you are:
use JPQL
use managed entities
use hard-coded nulls in the query string where you need to
Fortunately for the third option, with Hibernate you can use named parameters in native queries, so you don't have to do positional calculations for when something is available and when it isn't.
edit: 4th solution that I've discovered since.
You have your query:
Query q = em.createNativeQuery("UPDATE...");
Have some static final LocalDate object somewhere:
public static final LocalDate EPOCH_DATE = LocalDate.of(1970, 1, 1);
then call the query like this:
q.setParameter("start_date", EPOCH_DATE);
q.setParameter("start_date", nullableDateParam);
The first time you call setParameter for a parameter, Hibernate uses the class to resolve the type. The second time you call it, the type is already resolved, so a null will work.
It's an old question, but there is a more useful way:
your query...
.setParameter("transaction_id", null, LongType.INSTANCE)
It works.
Found from https://forum.hibernate.org/viewtopic.php?p=2493645
Going to newer versions of hibernate 5.1.17 and above + postgres seems to have exhibited this behavior. Looking into the code, when it binds a type that has no value, the old hibernate code attempted to resolve the type through a typeresolver. The newer versions of hibernate's implementation state that it will not guess.
public Type resolveParameterBindType(Object bindValue) {
if ( bindValue == null ) {
// we can't guess
return null;
}
We ended up just setting a default value based on the type first, and then the real null value.

Update counter in DB using JPA, avoiding manual synchronization

I have a Counter entity as below : for each prefix (current year and month), I am maintaining a counter that I need to increment.
#Entity
#Table(name = "T_COUNTER")
#SequenceGenerator(allocationSize = 1, name = "S_COUNTER", sequenceName = "S_COUNTER")
public class CodeCounter implements Serializable {
private static final long serialVersionUID = 6431190800245592165L;
#Id
#GeneratedValue(strategy = SEQUENCE, generator = "S_COUNTER")
#Column(name = "ID")
private Long id;
#Column(name = "PREFIX", nullable = false,unique = true)
private String prefix;
#Column(name = "COUNTER", nullable = false)
private Integer counter;
This is my very simple JPA repository, using Spring data :
public interface CodeCounterRepository extends JpaRepository<CodeCounter, Long> {
#Transactional
CodeCounter findByPrefix(String prefix);
}
Now, whenever my service is called, I need to get the right counter thanks to the prefix (and create it if it doesn't exist yet, the first time of the month), increment and save back. This is how I have implemented it so far :
//entry point in the service
public String generateUniqueRequestCode() {
System.out.println("Generating new Request Code for Consolidated Request");
Integer counter = getUniqueCodeAndUpdateCounter(calendarProvider);
String requestCode = format("%s_%04d_%02d_%06d", REQUEST_CODE_PREFIX, calendarProvider.getCurrentYear(), calendarProvider.getCurrentMonth(),
counter);
System.out.println("New Request Code for Consolidated Request:: " + requestCode);
return requestCode;
}
#Transactional
private synchronized Integer getUniqueCodeAndUpdateCounter(CalendarProvider calendarProvider) {
System.out.println("entering..");
String prefix = format("%04d_%02d", calendarProvider.getCurrentYear(), calendarProvider.getCurrentMonth());
CodeCounter codeCounter = codeCounterRepository.findByPrefix(prefix);
if (codeCounter != null) {
codeCounter.setCounter(codeCounter.getCounter() + 1);
} else {
codeCounter = new CodeCounter();
codeCounter.setPrefix(prefix);
codeCounter.setCounter(1);
}
CodeCounter counter=codeCounterRepository.save(codeCounter);
int result=counter.getCounter();
System.out.println("..exiting");
return result;
}
I've added a multithreaded unit test (using tempus fugit library ) with H2 DB, that shows it's working when 2 threads try to generate a unique code at the same time, but I'm not too happy with my code : I would like to get rid of that synchronized method and rely solely on proper transaction configuration.
If I remove the synchronized keyword, then both threads execute the method at same time and it fails because they generate the same prefix, which shouldn't happen (Unique index or primary key violation). Here's the log :
Generating new Request Code for Consolidated Request
Generating new Request Code for Consolidated Request
entering..
entering..
Hibernate: select codecounte0_.id as id1_4_, codecounte0_.counter as counter2_4_, codecounte0_.prefix as prefix3_4_ from t_counter codecounte0_ where codecounte0_.prefix=?
Hibernate: select codecounte0_.id as id1_4_, codecounte0_.counter as counter2_4_, codecounte0_.prefix as prefix3_4_ from t_counter codecounte0_ where codecounte0_.prefix=?
Hibernate: call next value for S_COUNTER Hibernate: call next value for S_COUNTER
Hibernate: insert into t_counter (counter, prefix, id) values (?, ?, ?)
Hibernate: insert into t_counter (counter, prefix, id) values (?, ?, ?)
..exiting
New Request Code for Consolidated Request:: CR_2016_09_000001
17:02:39.853 WARN SqlExceptionHelper - SQL Error: 23505, SQLState: 23505
17:02:39.854 ERROR SqlExceptionHelper - Unique index or primary key violation...
Any idea of how to implement this without synchronizing myself in the code ?
Is writing a method in your entity that is annotated with #PrePersist or #PreUpdate something that you are looking for?

Automatically Insert first row soon after table is created / recreated using Hibernate

I want Username = Administrator and Password = admin, soon after table is created (or whenever table is recreate).
Is there any way to insert first row using following code. I do not want to use a separate insert query
Is there any constraint in Hibernate to restrict user from deleting (first) row
#Entity
#Table(name = "Users")
public class Users implements Serializable {
#Id
#Column(name = "Username", unique = true, nullable = false)
private String username;
#Column(name = "Password", nullable = false)
private String password;
/**************** getter & setter ************/
}
What you seem to be looking for is called fixtures. With Hibernate you can supply import.sql file on your classpath with your initial data (insert statements).
There's a bit more information on the JBoss' site.
The import.sql file is a simple text file with SQL statements, one line per statement:
insert into users (id, username) values (1, 'administrator');
insert into users (id, username) values (2, 'user');
...

Categories

Resources