I'm getting the an OptimisticLockException when I try to update a managed entity EJB.
The EJB was fetched via:
port = entityManager.find(PortEntity.class, portID);
and then the EJB and the entityManager has been passed to a SAX ContentHandler so that in the endDocuent() method it can be updated. The ContentHandler has extracting the time zone information from the data returned from Google's Time Zone API server(s).
The code snippet is:
entityManager.refresh(port);
if (entityManager.contains(port))
log.info("Contained: " + port);
else
log.info("NOT Contained: " + port);
port.setTimezone(toTimezone);
entityManager.flush(); // <-- Line 70
And the log file show:
13:48:05,568 INFO [GeotimezoneHandler] Status: OK
13:48:05,569 INFO [GeotimezoneHandler] Raw offset: 3600.0000000
13:48:05,570 INFO [GeotimezoneHandler] DST offset: 0.0000000
13:48:05,570 INFO [GeotimezoneHandler] Timezone ID: Europe/Madrid
13:48:05,571 INFO [GeotimezoneHandler] Timezone Name: Central European Standard Time
13:48:05,577 INFO [GeotimezoneHandler] Contained: SeaPort[id=ESBCN, name=Barcelona]
13:48:05,591 ERROR [GeotimezoneHandler] Updating curise: javax.persistence.OptimisticLockException: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.nutrastat.voyager.entity.PortEntity$Sea#ESBCN]
at org.hibernate.ejb.AbstractEntityManagerImpl.wrapStaleStateException(AbstractEntityManagerImpl.java:1390) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Final]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1308) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Final]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1289) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Final]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1295) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Final]
at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:976) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Final]
at org.jboss.as.jpa.container.AbstractEntityManager.flush(AbstractEntityManager.java:439) [jboss-as-jpa-7.1.1.Final.jar:7.1.1.Final]
at com.nutrastat.voyager.util.GeotimezoneHandler.endDocument(GeotimezoneHandler.java:70) [voyager-lib.jar:]
So if the entityManager contains the EJB why after modifing it do I get the exception?
As always many thanks for your help
Steve
P.S.
I have looked at this thread and The MySQL database is using InnoDB, but I don't know how to execute the SELECT ##tx_isolation; command from within my code.
After two days of research I finally found what the problem was.
The entity class inherited from its superclass a javax.persistence.Version field. I had also hand injected data into the table, and because the version field was defined as allowing nulls had not bothered to insert a value, but one was needed.
Related
I have an ssis package which uses SQL command to get data from Progress database. Every time I execute the query, it throws this specific error:
ERROR [HY000] [DataDirect][ODBC Progress OpenEdge Wire Protocol driver][OPENEDGE]Internal error -1 (buffer too small for generated record) in SQL from subsystem RECORD SERVICES function recPutLONG called from sts_srtt_t:::add_row on (ttbl# 4, len/maxlen/reqlen = 33/32/33) for . Save log for Progress technical support.
I am running the following query:
Select max(ROWID) as maxRowID from TableA
GROUP BY ColumnA,ColumnB,ColumnC,ColumnD
I've had the same error.
After change startup-parameter -SQLTempStorePageSize and -SQLTempStoreBuff to 24 and 3000 respectively the problem was solved.
I think, for you the values must be changed to 40 and 20000.
You can find more information here. The name of the parameter in that article was a bit different than in my Database, it depends on the Progress-version witch is used.
I'm working on a java web project that uses:
Hibernate 5.2.2/JPA 2.0+ MySQL5InnoDBDialect
MySQL 5.6.15-innoDB (on EasyPHP/PHPMyAdmin) + JDBC connector 6.0.4
Joda time API 2.9.4 + Fasterxml jackson API 2.8.3
I'm facing a problem on inserting Time data on database. everytime i put a row, i get a +1H value on time column!
Attribute on Java:
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="HH:mm")
#Column(name = "RES_DUREE", nullable = false)
#Temporal(TemporalType.TIME) private Date resDuree;
Attribute on SQL:
RES_DUREE TIME NOT NULL;
EDIT (After Adrian Shum's Comment):
Connection line:
jdbc.url =
jdbc:mysql://localhost:3306/mydb?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
I do use UTC but it still 1H+ .
Any suggestion will help, thanks.
Normally it is caused by server and DB time zone mismatch.
In brief, java.util.Date does not contain Timezone information. Conceptually you can treat it as simply representing Date + Time (similar to what JODA/JSR310 LocalDateTime is doing). Therefore if your app server is UTC+10, and your DB is UTC+2, when you save a date of "2016-10-13 10:11:12", although your app server is treating it as "2016-10-13 10:11:12 +10:00", it is simply passing "2016-10-13 10:11:12" to DB. Given DB is UTC+2, it is thinking the time actually means "2016-10-13 10:11:12 +02:00". Situation become more messy if your JDBC connection is claimed to be "UTC+10", most DB is going to "smartly" translate "2016-10-13 10:11:12 +02:00" to "2016-10-13 18:11:12 +10:00" which caused weird time stored and retrieved.
You may diagnose by tracing the SQL (and the actual value used) for corresponding inserts and select. You should see discrepancies between the values, vs the value stored in table. Such tracing can be done by misc way, e.g.
Older version of Hibernate can show the parameter used in prepared statement by turning on proper logger
You may use tools like JdbcDsLog (Disclaimer: I am maintainer for a fork of JbdcDsLog at http://github.com/adrianshum/jdbcdslog)
There is probably tools in DBMS side to trace incoming queries.
Best way to solve is to make sure everything is in the same timezone, and the most rational choice for timezone is UTC.
I try to run this example but without using Redis, instead with my local MySQL server.
I have edited this spring boot app like this:
Gradle:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
}
}
apply plugin: 'spring-boot'
apply from: JAVA_GRADLE
//this 'if' statement is because I was getting error: Execution failed for task ':samples:findbyusername:findMainClass'.
//> Could not find property 'main' on task ':samples:findbyusername:run'.
if (!hasProperty('mainClass')) {
ext.mainClass = 'sample.FindByUsernameApplication'
}
tasks.findByPath("artifactoryPublish")?.enabled = false
group = 'samples'
dependencies {
compile("org.springframework.boot:spring-boot-starter-jdbc:$springBootVersion")
compile group: 'mysql', name: 'mysql-connector-java', version: '6.0.2'
compile group: 'org.springframework.session', name: 'spring-session', version: '1.2.0.RELEASE'
compile project(':spring-session'),
"org.springframework.boot:spring-boot-starter-web",
"org.springframework.boot:spring-boot-starter-thymeleaf",
"nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect",
"org.springframework.security:spring-security-web:$springSecurityVersion",
"org.springframework.security:spring-security-config:$springSecurityVersion",
"com.maxmind.geoip2:geoip2:2.3.1",
"org.apache.httpcomponents:httpclient"
testCompile "org.springframework.boot:spring-boot-starter-test",
"org.assertj:assertj-core:$assertjVersion"
integrationTestCompile gebDependencies,
"org.spockframework:spock-spring:$spockVersion"
}
def reservePort() {
def socket = new ServerSocket(0)
def result = socket.localPort
socket.close()
result
}
application.properties
spring.datasource.url = jdbc:mysql://localhost:3306/TEST?characterEncoding=UTF-8&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=but
spring.thymeleaf.cache=false
spring.template.cache=false
HttpSessionConfig.java
#EnableJdbcHttpSession // <1>
public class HttpSessionConfig {
}
Application starts on tomcat but when I hit localhost in my browser I get:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Mon May 23 21:14:31 CEST 2016
There was an unexpected error (type=Internal Server Error, status=500).
PreparedStatementCallback; bad SQL grammar [INSERT INTO SPRING_SESSION(SESSION_ID, CREATION_TIME, LAST_ACCESS_TIME, MAX_INACTIVE_INTERVAL, PRINCIPAL_NAME) VALUES (?, ?, ?, ?, ?)]; nested exception is java.sql.SQLSyntaxErrorException: Table 'test.spring_session' doesn't exist
I don't remember reading anything about manually creating this table so I assumed that spring will handle it for me...
EDIT:
I actually tried to manually create tables and then application runs OK. But I guess I shouldn't be doing this manually.
Spring Session ships with database schema scripts for most major RDBMS's (located in org.springframework.session.jdbc package), but the creation of database tables for Spring Session JDBC supports needs to be taken care of by the users themselves.
The provided scripts can be used untouched, however some users may choose to modify them to fit their specific needs, using the provided scripts as a reference.
An option would be to use a database migration tool, such as Flyway, to handle the creation of database tables.
Since you're using Spring Boot, it might be of your interest that there is a pending PR to add support for automatic initialization of Spring Session JDBC schema: https://github.com/spring-projects/spring-boot/pull/5879
If the documentation misled you into thinking the tables should be created automatically by Spring Session itself, consider reporting the issue so we can update the documentation if necessary: https://github.com/spring-projects/spring-session/issues
At least as of now, you don't have to create tables manually.
In my test, tables were created automatically after adding the following line into the file application.properties when this file appears like shown above.
spring.session.jdbc.initialize-schema=always
I found this beautiful line from the following stackoverflow page.
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'test.spring_session' doesn't exist - Spring Boot
I am sorry that I don't know if it was necessary to create tables manually in 2016 or 2017. I will update this answer when I get to know this or get to have some more fruitful related information. I am just wishing that nobody will be led to an idea that automatic creation of session tables is impossible with the lastest Spring Framework version of 2019 or later.
spring.session.jdbc.initialize-schema=always worked for me
I already made triggers to log the actions before persisting, updating and deleting the mapped entities, but it's only within MySQL, so I think I must do an "application-level trigger" using annotations #PostPersist, #PostUpdate and #PostDelete.
So, when entity e.g. Category gets persisted, a method for inserting info into a log table is thrown, with the following SQL:
INSERT INTO log (date_hour, table, id_tuple, user)
VALUES (NOW(), 'category', " + id + ", '" +
FacesContext.getCurrentInstance().getExternalContext().getRemoteUser() + "')";
I made exactly like that, using createNativeQuery then query.executeUpdate() but nothing happened.
What's the best approach for doing what I want? Reminding that I'm using EclipseLink.
You can log all changes to an entity type with little effort using EclipseLink's History policy: http://wiki.eclipse.org/EclipseLink/Examples/JPA/History
Assuming you are CDI you can create an interceptor like described in: Oracle Tutorial CDI Interceptor
In this interceptor you can create your insert in the log table.
But keep in mind that logging usually slows an application down much. Consider using loglevels that you normally just log errors instead of everything
I have a Message object that has log entries that are added as the message is processed.
Domain class Message has:
SortedSet messageLogEntries
static hasMany = [messageLogEntries: MessageLogEntry]
void addLogEntry(String entry) {
def mle = new MessageLogEntry(logEntry: entry)
this.addToMessageLogEntries(mle)
this.save(failOnError: true, flush: true)
log.debug(entry)
}
I can step through the code the entry is created and saved and allocated an id but when I query the database in MySql the entry is not there.
This was working but is not since I converted from mysql 5.5 to 5.6.10
Please help.
If this code is being executed inside a Hibernate transaction, you will not see it in the DB until the transaction is committed. Are you running this code inside an integration test or from a transactional execution stream (e.g. a service)?