Numeric types mapping issue in Spring Data R2dbc with postgres - postgresql

I tried to use Spring Data R2dbc/Postgres in a sample application.
Spring Boot 2.4.0-M2
R2dbc Postgres (managed by Spring Boot)
Spring Data R2dbc 1.2.0-M2(managed by Spring Boot)
The table scripts.
CREATE SEQUENCE IF NOT EXISTS ORDERS_ID_SEQ;
CREATE TABLE IF NOT EXISTS ORDERS(
ID INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('ORDERS_ID_SEQ') ,
CUST_ID BIGINT NOT NULL,
AMOUNT REAL NOT NULL
);
ALTER SEQUENCE ORDERS_ID_SEQ OWNED BY ORDERS.ID;
The data.sql:
-- INSERT SAMPLE DATA
DELETE FROM ORDERS;
INSERT INTO ORDERS(CUST_ID, AMOUNT) VALUES (1, 100.2);
I use a ResourceDatabasePopulator to populate the data, it works.
But when I was trying to save the data by Repository, failed.
#Table(value = "ORDERS")
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class Order implements Serializable {
#Id
#Column(value = "ID")
private Integer id;
#Column(value = "CUST_ID")
private Long customerId;
// use BigDecimal or Java Money API in the real-world application.
#Column(value = "AMOUNT")
private Double amount;
}
public interface OrderRepository extends R2dbcRepository<Order,Integer> {
}
// in application runner.
orders .save(Order.builder().customerId(c.getId()).amount(201.0).build())
It threw an exception like this:
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.UnsupportedOperationException: Binding parameters is not supported for the statement 'INSERT INTO ORDERS (CUST_ID, AMOUNT) VALUES (?, ?)'
Caused by: java.lang.UnsupportedOperationException: Binding parameters is not supported for the statement 'INSERT INTO ORDERS (CUST_ID, AMOUNT) VALUES (?, ?)'
at io.r2dbc.postgresql.SimpleQueryPostgresqlStatement.bind(SimpleQueryPostgresqlStatement.java:78) ~[r2dbc-postgresql-0.8.4.RELEASE.jar:0.8.4.RELEASE]
at io.r2dbc.postgresql.SimpleQueryPostgresqlStatement.bind(SimpleQueryPostgresqlStatement.java:44) ~[r2dbc-postgresql-0.8.4.RELEASE.jar:0.8.4.RELEASE]
The complete codes is here.
Updated: Give up extending from AbstractR2dbcConfiguration, and get resovled when following the official guide.

Related

Can't find a existing line using JPA with H2 based on UUID criteria

I am writing a junit test using Spring Boot JPA.
My entity has an attribute of type UUID (BaseEntity defines a Long id which is a sequence in database). My mapping is in a orm.xml file:
public class User extends BaseEntity {
private String username;
private UUID uuid;
...
}
I have defined a UserRepoImpl class that search for a User using a given UUID (jpaRepo being an interface extending JpaRepository<User,Long>:
public Optional<User> getUserByUuid(UUID aUuid) {
return jpaRepo.findByUuid(aUuid);
}
I have written a junit to test this method against a H2 database and I use a sql file to insert data before the test :
#ActiveProfiles("tu")
#ExtendWith(SpringExtension.class)
#DataJpaTest(includeFilters = #ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Repository.class))
#Sql("/sql/infra/repo/user-repository.sql")
public class UserRepositoryImplTest {
#Autowired
private UserRepositoryImpl cut;
#Test
void should_ReturnUser_WhenUUIDExist() {
UUID uuid = UUID.fromString("9fc1cd41-9d28-463f-94b9-542836572802");
Optional<User> user = cut.getUserByUuid(uuid);
Assertions.assertTrue(user.isPresent());
Assertions.assertEquals(1L, user.get().getId());
Assertions.assertEquals(uuid, user.get().getUuid());
}
}
The SQL file inserts a user with a UUID that I have converted :
INSERT INTO USERS (id, USERNAME, UUID)
VALUES (3, 'user3', X'9FC1CD419D28463F94B9542836572802');
The test fails because getUserByUuid() doesn't return any user.
What I don't understand is why the UUID column is generated in H2 with a binary type when there is a UUID type in H2 :
Hibernate: create table users (id bigint not null, username varchar(255), uuid binary(255), account_id bigint, primary key (id))
I tried to use varchar for storing the UUID and that works but I don't want to use varchar to store my UUID.
I use https://www.piiatomi.org/uuid_converter.html to make conversion between UUID and hex.
Any idea ?
Thank you!
binary(255) is a fixed-length binary string (byte array in Java) with exactly 255 bytes. This is a wrong data type for UUID values, but some versions of Hibernate ORM incorrectly choose it for UUID properties.
You can override this default with some correct type:
#Column(columnDefinition="UUID")
private UUID uuid;

Hibernate postgres auto increment after manual insert

I have a basic spring application, with a simple entity. I have a flyway script, to create the postgres table, and add some starting data.
create table user (
id serial primary key,
username varchar (50) unique not null,
password varchar (150) not null
);
insert into user (id, username, password) values (1, 'name', 'somehashed');
insert into etc...
I've set up my entity as follows:
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, columnDefinition = "serial")
private Long id;
...
other fields, constructor, getters setters etc...
My problem is, that on start-up, the basic entities are persisted by flyway, but upon trying to save a new entity, hibernate tries to give it the ID 1, although it is already given to another one.
I tried it also with SEQUENCE strategy, the problem didn't get solved.
Ok, problem was that I specified explicitly the ID I wanted to give while the insert script, and I didn't let postgres do the magic...

Getting Exception while inserting data in Redshift DB

while fetching data from database It is working fine when trying to insert data in Redshift database getting exception
MY POJO class : Quote.java
#Entity
public class Quote {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Integer id;
#Column(name="user_name")
private String username;
private String quote;
}
Getting Exception :
com.amazon.support.exceptions.ErrorException: [Amazon](500310) Invalid operation: relation "hibernate_sequence" does not exist;
We cannto create sequence in Redshift but we can create sequence in postgresql
and when I am connect with redshift + spring-data-jpa then it is using postgresql db
hbm2ddl.auto = update
I have tried with identity also
I did GenerationType.TABLE and created table called sequence to overcome this.
CREATE TABLE sequence(seq_count bigint, seq_name varchar(65535));
But it seems to be very slow.

How can I update a row which EclipseLink uses for #TableGenerator or insert a new row into that table?

I want to integrate a JDBC based application into an Java EE application running in GlassFish which uses EclipseLink JPA 2.5 with MariaDB (a MySQL clone).
The JDBC based application imports data from flat files into some database tables and requires to update the next serial value for the primary keys by executing:
UPDATE dbtable SET nextid = 4711 WHERE name = 'table4Import';
using a standard JDBC connection. This statement throws an SQL timeout exception because the row is locked. It seems to be locked by EclipseLink which uses the same table for its table generator. Here is (parts of) the table containing the serial values:
CREATE TABLE dbtable (
dbtid bigint not null primary key,
dbtname varchar ( 50 ) not null,
dbtnextid bigint );
This is the code for one of the JPA entities:
#Entity
#Access(AccessType.PROPERTY)
#Table( name = "table4Import" )
public class Table4ImportClass {
private Long id = null;
#NotNull
#Id
#TableGenerator( name="table4Import", allocationSize=1,
table="dbtable", pkColumnName="dbtname",
pkColumnValue="table4Import", valueColumnName="nextid" )
#GeneratedValue( generator="table4Import", strategy=GenerationType.TABLE )
public Long getId() { return this.id; }
...
Addendum: It is not even possible to insert a new row into that table. EclipseLink seems to lock the complete table. It is still possible to modify the table (insert & update) from another program. The lock scope seems to be the JVM in which EclipseLink is running.
Addendum 2: Put this as a bug into EclipseLink bug tracker 455756

Eclipselink JPA gets error when inserting into Derby table with generated primary key

EclipseLink seems to be incorrectly passing a null primary key value to Derby when persisting into a table with generated primary key. Derby returns error of Attempt to modify an identity column in this case. Derby needs a SQL statement that excludes the id value. My question is how do I coerce EclipseLink to send the right SQL? Details follow...
I am using Eclipselink to map to a Derby database (v10.8.1.2) under the latest Netbeans 7.3beta2.
The database table primary key is auto generated:
CREATE TABLE STUDENT_BATCH (
ID bigint PRIMARY KEY
GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
FILENAME varchar(200) NOT NULL,
SCHOOLBOARD varchar(100) NOT NULL,
CREATE_TS timestamp NOT NULL,
CONTACT_INFO varchar(200),
NOTES varchar(2000),
BOARD_NAME varchar(100)
)
;
The corresponding jpa class specifies that the id is generated using strategy of identity:
#Entity
#Table(name = "STUDENT_BATCH")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "StudentBatch.findAll", query = "SELECT s FROM StudentBatch s")})
public class StudentBatch implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Basic(optional = false)
#Column(name = "FILENAME")
private String filename;
#Basic(optional = false)
#Column(name = "SCHOOLBOARD")
private String schoolboard;
#Basic(optional = false)
#Column(name = "CREATE_TS")
#Temporal(TemporalType.TIMESTAMP)
private Date createTs;
#Column(name = "CONTACT_INFO")
private String contactInfo;
#Column(name = "NOTES")
private String notes;
#Column(name = "BOARD_NAME")
private String boardName;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "studentBatchId")
private Collection<StudentRecord> studentRecordCollection;
methods etc follow...
When I go to persist the class, I do not specify an id so that Derby will provide the id.
StudentBatch sb = new StudentBatch();
sb.setBoardName(meta.get("BOARD NAME"));
sb.setContactInfo(meta.get("CONTACT INFO"));
sb.setCreateTs(new Date());
sb.setFilename(event.getFile().getFileName());
sb.setNotes(meta.get("NOTES"));
sb.setSchoolboard(meta.get("SCHOOL BOARD"));
_logger.debug("persisting batch");
em.persist(sb);
_logger.debug("flushing");
em.flush();
_logger.debug("flushed");
Eclipselink, however, passes the id to derby as null and Derby gives error Attempt to modify an identity column:
INFO: DEBUG 11710 27 Nov 2012 18:17:10,558 [http-thread-pool-8080(4)] (FileUploadController.java:75) - persisting batch
INFO: DEBUG 11713 27 Nov 2012 18:17:10,561 [http-thread-pool-8080(4)] (FileUploadController.java:77) - flushing
WARNING: Local Exception Stack:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLSyntaxErrorException: Attempt to modify an identity column 'ID'.
Error Code: -1
Call: INSERT INTO STUDENT_BATCH (ID, BOARD_NAME, CONTACT_INFO, CREATE_TS, FILENAME, NOTES, SCHOOLBOARD) VALUES (?, ?, ?, ?, ?, ?, ?)
bind => [7 parameters bound]
Query: InsertObjectQuery(ca.ontario.mhltc.studentupload.model.StudentBatch[ id=null ])
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:333)
This makes some sense to me since if I try to execute an insert on sql command line with the id field specified I get an error too.
INSERT INTO STUDENT_BATCH (id, BOARD_NAME, CONTACT_INFO, CREATE_TS, FILENAME, NOTES, SCHOOLBOARD)
VALUES (null, 'abc', 'def', current_timestamp, 'aaa', 'aabb', '1234');
gives me this:
Error code -1, SQL state 42Z23: Attempt to modify an identity column 'ID'.
Line 1, column 1
Execution finished after 0 s, 1 error(s) occurred.
however, this insert statement is successful:
INSERT INTO STUDENT_BATCH (BOARD_NAME, CONTACT_INFO, CREATE_TS, FILENAME, NOTES, SCHOOLBOARD)
VALUES ('abc', 'def', current_timestamp, 'aaa', 'aabb', '1234');
returns
Executed successfully in 0.002 s, 1 rows affected.
Line 1, column 1
Execution finished after 0.002 s, 0 error(s) occurred.
Seems to me that Eclipselink should know about how Derby handles identity columns and should not pass the id column at all on the insert statement. Is there a workaround for this or I should I just dump Derby and use some other database?
EclipseLink does not include the Id in the INSERT for Derby IDENTITY, so you have something odd going on.
Did you previously use another generator strategy and not recompile/deploy your code correctly?
Also try setting your platform using the"eclipselink.target-database"="Derby" in your persistence.xml.