how to do batch insert/update with Springboot data JPA and Mysql - mysql

I'm trying to implement batch insert/update records with SpringBoot Data Jpa with Mysql,
here is my yml config:
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.jdbc.batch_size=500
spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true
spring.jpa.properties.hibernate.generate_statistics=true
And I use an mysql auto increment column as primary key, here
public class Customer implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
I've googled that batch ops will not work with GenerationType.IDENTITY,
but also I notice that mysql not support GenerationType.SEQUENCE
then how could I accomplish bath insert/update with jpa's saveAll(data) method with mysql DB
Thank you

I've googled that batch ops will not work with GenerationType.IDENTITY, but also I notice that mysql not support GenerationType.SEQUENCE
Set the id in the application. For example using a UUID

Related

Hibernate store YearMonth in MySQL as Blob instead of text

A Spring Boot 2.3.0 Project is Storing Data in MySQL 8.0. (jdk 9)
A JPA Class has Java 8 YearMonth field
#Data
#Entity
public class Version {
...
private YearMonth releaseDate;
private LocalDateTime createdAt;
When I persist an instance of version, it's stored like this:
As You can see LocalDateTime gets stored as text but YearMonth as Blob. Now When I get back the entity it works fine and I do get YearMonth value.
The problem is that I cannot read it through MySQL Workbench. And I know that EclipseLink (official implementation of JPA) does persist YearMonth as Text.
How Can I store it as Text and not BLOB?

Change spring jpa existing table column type

I'm using spring boot and jpa and I have an existing table with attribute as VARCHAR 255:
#Column(name = "description")
private String description;
I tried to change it to longtext with no success:
#Column(name = "description", columnDefinition = "longtext")
private String description;
My configuration in the application.properties file regarding Hibernate ddl is set to update:
spring.jpa.hibernate.ddl-auto = update
Is it possible to change VARCAR 255 to longtext? if it does, How do I do it?
Thanks,
Avi
spring.jpa.hibernate.ddl-auto=update
will not do following changes
Delete a column which is no longer in entity
Modify existing column.
Refer here for more details
spring-data-jpa ddl ‘update’ will not change exist colums,neither update nor delete.
I also faced the same issue
Your current configuration won't update
The best way to do this is through migrations using flyway
Documentation : https://flywaydb.org/documentation/
you can write sql queries in your project and alter the same column in your entity.

JPA Hibernate - Multiple Database Dialects and nvarchar(length) data type

I have to do a project using JPA + Hibernate in which I'm using 3 dialects: MySQL5InnoDBDialect, MSSQL2012Dialect and Oracle12cDialect.
Right now I have a specification which is telling me that for some column from:
Oracle database, I have to use NVARCHAR2(LENGTH) data type
MySql database, I have to use VARCHAR(LENGTH) data type
MSSQL database, I have to use NVARCHAR(LENGTH) data type
... and here is my problem..
If I use:
#Column(name="columnName" length = 255)
private String columnName;
hibernate generates varchar(255) and this is good just for MySQL
If I use:
#Column(name="columnName", columnDefinition="nvarchar2(255)")
private String columnName;
it's not possible in MySQL, i get error because of columnDefinition, but in oracle is okay
I tried to customize MySQL dialect creating
public class CustomMySQL5InnoDBDialect extends MySQL5InnoDBDialect{
public CustomMySQL5InnoDBDialect() {
super();
registerColumnType(Types.NVARCHAR, "nvarchar2($l)");//$l not $1
registerHibernateType(Types.NVARCHAR, StandardBasicTypes.STRING.getName());
}
}
and giving this class in hibernate configuration for MySQL dialect.
I have the same problem in MySQL if I'm using columnDefinition property.
Can you help with this problem please?
The solution is to make use of the feature that the JPA API spec provides you with for just this situation. Define a file orm.xml for each datastore that you need to support, and enable the requisite one when using each database. See this link for details of the file format. That way you don't need to think about hacking the internal features of whichever JPA provider you are using, and you also retain JPA provider portability, as well as database portability
The idea of putting schema specific information info (static) Java annotations is an odd one, even more so when wanting database portability.

EclipseLink: "Updates are not allowed" on a native query INSERT INTO

since upgrading to EclipseLink 2.5 from EL 2.1 we get a PersistenceException on a previously running code.
The scenary is really simple: we have two identical tables, the only difference is that one is a "history" version of the other. Basically, when we are sure that a row won't change anymore, we move the row to the history table. This means that while the id is generated on the first table, it is "inherited" on the history table. Here are the entities:
#Entity
#Table(name = "DOCUMENT")
public class Document implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
// Some other fields
}
#Entity
#Table(name = "DOCUMENT_HISTORY")
public class DocumentHistory implements Serializable {
#Id
private long id;
// Some other fields
}
In order to move a row to the history table we use a native query (because some columns are not mapped as fields in the entity classes):
String query = "INSERT INTO DOCUMENT_HISTORY SELECT * FROM DOCUMENT t WHERE t.id=?1";
Query updateQuery = entityManager.createNativeQuery(query);
updateQuery.setParameter(1, document.getId());
updateQuery.executeUpdate();
When executing this query, sometimes this exception is thrown:
javax.persistence.PersistenceException: Exception [EclipseLink-7251] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5):
org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [id] of class [org.myc.entities.jpa.company.DocumentHistory] is mapped to a primary key column in the database. Updates are not allowed.
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:868)
at org.eclipse.persistence.internal.jpa.QueryImpl.performPreQueryFlush(QueryImpl.java:963)
at org.eclipse.persistence.internal.jpa.QueryImpl.executeUpdate(QueryImpl.java:296)
at org.myc.utility.jpa.user.DocumentManager.toDocumentHistory(DocumentManager.java:141)
at java.lang.Thread.run(Thread.java:662)
Caused by: Exception [EclipseLink-7251] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [id] of class [org.myc.entities.jpa.company.DocumentHistory] is mapped to a primary key column in the database. Updates are not allowed.
at org.eclipse.persistence.exceptions.ValidationException.primaryKeyUpdateDisallowed(ValidationException.java:2548)
at org.eclipse.persistence.mappings.foundation.AbstractDirectMapping.writeFromObjectIntoRowWithChangeRecord(AbstractDirectMapping.java:1257)
at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildRowForUpdateWithChangeSet(ObjectBuilder.java:1768)
at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.updateObjectForWriteWithChangeSet(DatabaseQueryMechanism.java:1030)
at org.eclipse.persistence.queries.UpdateObjectQuery.executeCommitWithChangeSet(UpdateObjectQuery.java:84)
at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:301)
at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899)
at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:798)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1793)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1775)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1726)
at org.eclipse.persistence.internal.sessions.CommitManager.commitChangedObjectsForClassWithChangeSet(CommitManager.java:267)
at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsForClassWithChangeSet(CommitManager.java:192)
at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:138)
at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4196)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1441)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithPreBuiltChangeSet(UnitOfWorkImpl.java:1587)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.writeChanges(RepeatableWriteUnitOfWork.java:452)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:863)
... 5 more
Did anyone have the same problem? Why do you think this doesn't happen on all rows but only some of them (with no apparent specific characteristics)?
Thanks in advance,
Luc
P.S.: Is it correct for EL to perform mapping/query validation on a native query?
As stated in JPA 2.0 specification, executeUpdate() executes an update or delete statement thus insert is not supported.
You can insert an entity into the underlying database by persisting and flushing it with use of EntityManager (within a transaction). An dummy example in your particular case might look as follows:
// transaction starts
Document doc = em.find(Document.class, id);
DocumentHistory docHistory = new DocumentHistory(doc);
em.persist(docHistory);
// transaction ends
NOTE: instead of a proprietary solution (which is fine) you may consider of using EclipseLink's auditing feature:
EclipseLink also support full history support, which allows a complete history of all changes made to the database to be tracked in a mirror history table.
Ok, I found the problem and it wasn't related at all with that query, so I still don't know why the exception is thrown there.
The problem was that instead of using entityManager.find() to get the newly inserted history row, we were creating a DocumentHistory instance (with same ID) which was not attached to a persistence context, then using that instance to perform other operations.
Hope it helps someone.

Setting column length of a Long value with JPA annotations

I'm performing a little database optimisation at the moment and would like to set the column lengths in my table through JPA. So far I have no problem setting the String (varchar) lengths using JPA as follows:
#Column(unique=true, nullable=false, length=99)
public String getEmail() {
return email;
}
However, when I want to do the same for a column which is of type Long (bigint), it doesn't work. For example, if I write:
#Id
#Column(length=7)
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
The column size is still set as the default of 20. Are we able to set these lengths in JPA or am I barking up the wrong tree?
Thanks,
Gearoid.
precision, scale make more sense for a numeric type. Also depends whether the JDBC driver and RDBMS allows setting of those on the particular column type
Are you using Eclipse as your IDE? If so, I suggest you make use of the Dali plugin (already installed) and active the "JPA Details" view. This view will help guide you as to what attributes you need to plug into your annotations.
You could have scale=7 instead of length=7. However JPA is still going to tell the database to prepare to hold an int of scale 20.
Also make sure you have your database dialect properly set.