How entity manager read new record inserted by others - java

My application use JPA/hibernate to read data from database. The application is read only, and data is inserted by other program.
The problem is that my application can only read flesh data in the first time. When new data is inserted by other program, my application cannot see it.
Here is my test code:
public class TestJpaRead {
private static EntityManagerFactory emf = Persistence.createEntityManagerFactory("org.hibernate.tutorial.jpa");
public static void main(String[] args) {
LOG.debug("first time");
countRow(); //output row size = X
//set break point here, and manually insert an new row by using mysql client
LOG.debug("second time");
countRow(); //should output row size = X + 1, but it is still X
}
public static void countRow() {
EntityManager em = emf.createEntityManager();
Query query = em.createQuery("SELECT a FROM " + Report.class.getSimpleName() + " a");
List result = query.getResultList();
LOG.debug("countRow: {}", result.size());
em.close();
}
}
and here is my persistence.xml (nothing special):
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="org.hibernate.tutorial.jpa" transaction-type="RESOURCE_LOCAL">
<description>
Persistence unit for the JPA tutorial of the Hibernate Getting Started Guide
</description>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/foo" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="bar" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="auto" />
</properties>
</persistence-unit>
Thanks!

From MySQL query log, I find the reason of the problem:
48 Query SET autocommit=0
140606 11:35:41 48 Query select report0_.id from Report report0_ /*countRow()*/
48 Query SHOW WARNINGS
140606 11:35:42 48 Query select report0_.id from Report report0_ /*countRow()*/
48 Query SHOW WARNINGS
Hibernate does not work in the autocommit mode by default.
em.close() does not implicit commit or rollback the transaction, i.e., the JDBC connection and transaction is still alive/open.
This is what I misunderstood. (emf.close() will actually close the connection.)
When you get EntityManager from emf.createEntityManager(), the new
EntityManager may reuse old JDBC connection. It means that you may
in the transaction opened by previous closed EntityManager.
When you are in a uncommit/opened transaction, and use the default
MySQL isolation level, you cannot see change made by others.
Solution: explicit open and commit the transaction, or tell Hibernate to allow autocommitted JDBC connections. Refs: Select using hibernate

Related

How to add missing columns in a multi-schema based multi-tenant web app using eclipselink

I'm developing a multi-tenant web app with "Shared Database/Separate Schemas" approach using java, jpa(eclipselink), mysql. My persistence file looks like:
<persistence-unit name="GroupBuilderPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="eclipselink.cache.shared.default" value="false"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/?"/>
<property name="eclipselink.ddl-generation" value="create-or-extend-tables"/>
<--- Here goes other properties definition -->
</persistence-unit>
Now here is my EntityMangerFactory and EntityManager:
emfForTenant = Persistence.createEntityManagerFactory("GroupBuilderPU");
EntityManager em = emfForTenant.createEntityManager();
em.setProperty("eclipselink.tenant-id", schemaNameAsTenantId);
Its working fine untill I'm adding any new persistence column in any entity.
Like I've a Entity UserAccount where I've added a new column 'String rentalinfo' :
#Entity
#Multitenant(MultitenantType.TABLE_PER_TENANT)
#TenantTableDiscriminator(type = TenantTableDiscriminatorType.SCHEMA, contextProperty = PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT)
public class UserAccount implements Serializable {
...
private String rentalinfo;//Newly added column
...
}
Now after that this the following line is giving error:
em.createQuery("SELECT ua FROM UserAccount ua").getResultList();
The error is:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'RENTALINFO' in 'field list'
So what will be the solution for adding new column (extend table) in this approach?
You are getting this exception because the 'RENTALINFO' column does not exist on your UserAccount table. Under normal circumstances, setting "create-or-extend-tables" will have EclipseLink issue an ALTER to your existing table, adding the new column. However, it would appear ddl generation is not supported for MultitenantType.TABLE_PER_TENANT: https://wiki.eclipse.org/EclipseLink/DesignDocs/Multi-Tenancy/TablePerTenant
Not supported:
Schema generation will not be supported since it requires knowledge of all the tenants (schema's) and further to that, access provision must be set once the tables are created if using schema level table per tenant.
So there is no ALTER and your table does not have the column.
As a side note, you can turn on EclipseLink SQL logging using the following persistence properties:
<properties>
<property name="eclipselink.logging.level" value="ALL"/>
<property name="eclipselink.logging.level.sql" value="FINE"/>
<property name="eclipselink.logging.parameters" value="true"/>
</properties>
This way, you can see what queries EclipseLink is (or in this case, isn't) executing.

Hibernate can't generate id

I've created a new project on my IntelliJ IDEA, using hibernate (+ hibernate entitymanager) 5, hsqldb 1.8 (for testing only), as well as junit 4.
I've got my test class
UserRoundRelationsTest
EntityManagerFactory emf;
EntityManager em;
Date currentDate;
User user;
Round round1;
Round round2;
And some #After and #Before methods:
#Before
public void saveDummyUserAndTwoDummyRounds() {
emf = Persistence.createEntityManagerFactory("testPersistence");
em = emf.createEntityManager(); // Retrieve an application managed entity manager
currentDate = new Date();
user = new User.UserBuilder("alias").fullName("Full Name").phoneNumber("Phone Number").height(100).weight(100).birthDate(currentDate).build();
round1 = new Round(new GameMode(1, ButtonTargetStrategy.RANDOM, ButtonSkipStrategy.SKIP_ON_MISTAKE));
round2 = new Round(new GameMode(2, ButtonTargetStrategy.RANDOM, ButtonSkipStrategy.SKIP_ON_MISTAKE));
round1.setUser(user);
round2.setUser(user);
HashSet<Round> rounds = new HashSet<Round>(2);
rounds.add(round1);
rounds.add(round2);
em.getTransaction().begin();
user.setRounds(rounds);
em.persist(user);
em.persist(round1);
em.persist(round2);
em.flush();
em.getTransaction().commit();
}
and
#After
public void deleteData() {
em.remove(user);
em.remove(round2);
em.remove(round2);
em.getTransaction().commit();
em.close();
emf.close();
}
Firstly, when I try to run the test, hibernate can't find the tables for User and Round classes, as well as everything they are dependent on (a class called ButtonPress for example). I assume this happens due to the automatic create-drop I have configured at my persistence.xml
<persistence-unit name="testPersistence">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>com.carloworks.model.User</class>
<class>com.carloworks.model.Round</class>
<class>com.carloworks.model.ButtonPress</class>
<properties>
<!--<property name="hibernate.archive.autodetection" value="class,hbm"></property>-->
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"></property>
<property name="hibernate.show_sql" value="true"></property>
<property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"></property>
<property name="hibernate.connection.username" value="sa"></property>
<property name="hibernate.connection.password" value=""></property>
<property name="hibernate.connection.url"
value="jdbc:hsqldb:mem:buttonMasherModelsTestDb;MV_STORE=FALSE;MVCC=FALSE"></property>
<!--<property name="hibernate.default_schema" value="buttonMasherModelsTestDb"/>-->
<property name="hibernate.hbm2ddl.auto" value="create-drop"></property>
</properties>
</persistence-unit>
Scrolling further down the error logs, I also get an error at this line:
em.persist(user);
null identifier (plus 'This function is not supported'/'GenericJDBCException: could not prepare statement').
What did I do wrong?
The project is also available on Github
Full Error Log
Changing the version to the latest one fixed the problem
*Some may experience a broken path error (caused by maven). If you are using intelliJ navigate to Project Settings -> Modules and Fix any problems under the "Problems" tab.

Error with persistence using EclipseLink and UCanAccess

I am trying to develop an app for exercise reasons. I am using MSAccess 2010 as the database with UCanAccess (3.06) as the driver and the EclipseLink 2.1 as the entity framework.
I am stuck in adding new records to the database. Here the error code:
Internal Exception: net.ucanaccess.jdbc.UcanaccessSQLException: UCAExc:::3.0.6 user lacks privilege or object not found: IDENTITY_VAL_LOCAL
Error Code: -5501
Call: SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1
Query: ValueReadQuery(name="SEQ_GEN_IDENTITY" sql="SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1")
It seems to me that the autogenerate of the id fails. The entity class was generated vie Netbeans and looks like this:
#Transient
private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Integer id;
By default, EclipseLink tries to automatically detect the underlying database and generate SQL statements using the appropriate SQL dialect. That apparently isn't working for you because the SQL statement to retrieve the last created identity value is not recognized by UCanAccess .
You could try adding a target-database directive to your EclipseLink configuration specifying SQLServer in an attempt to get a working SQL statement (SELECT ##IDENTITY) to retrieve the last created ID value. However, bear in mind that there are significant differences between T-SQL and Access SQL so you will probably continue to encounter other compatibility issues between EclipseLink and UCanAccess.
before knowing above answer i was also facing same problem for inserting new record in access Database ,
Thanks to Mr. Gord Thompson to give a great Solution for me ,
and it is working too.
i have just added one line in my persistence.xml file..
property name="eclipselink.target-database" value="HSQL"
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="OnePU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>design_frames.One</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:ucanaccess://C:\One\One.accdb"/>
<property name="javax.persistence.jdbc.user" value=""/>
<property name="javax.persistence.jdbc.driver" value="net.ucanaccess.jdbc.UcanaccessDriver"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="eclipselink.target-database" value="HSQL"/>
</properties>
</persistence-unit>
</persistence>

Hibernate with Sql Server fail for nvarchar field with "No Dialect mapping..."

I'm using Hibernate's JPA-Implementation to access our SQL Server 2012 database.
When trying to select a nvarchar field in a native query, I get an exception "No Dialect mapping for JDBC type: -9".
It looks much like No Dialect mapping for JDBC type: -9 with Hibernate 4 and SQL Server 2012 or No Dialect mapping for JDBC type: -9 but I couldn't find a solution for me there (both are not using JPA).
My database setup:
CREATE TABLE NvarcharExample(
exampleField nvarchar(20) PRIMARY KEY
)
INSERT INTO NvarcharExample(exampleField) VALUES ('hello')
My code:
import java.io.IOException;
import javax.persistence.*;
#Entity
class NvarcharExample {
#Id
public String exampleField;
}
public class NvarcharTest {
public static void main(String[] args) throws IOException, InterruptedException {
String queryString = "SELECT e.exampleField FROM NvarcharExample e";
// establish connection
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("persistenceUnit");
try {
EntityManager entityManager = entityManagerFactory.createEntityManager();
// access data using JPQL
entityManager.createQuery(queryString).getResultList(); // works
// access data using SQL (native query)
entityManager.createNativeQuery(queryString).getResultList(); // fails
} finally {
entityManagerFactory.close();
}
}
}
My persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="persistenceUnit">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<!-- database connection settings -->
<property name="javax.persistence.jdbc.driver" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
<property name="javax.persistence.jdbc.url" value="jdbc:sqlserver://<servername>:<port>;databaseName=<databasename>" />
<property name="javax.persistence.jdbc.user" value="<user>" />
<property name="javax.persistence.jdbc.password" value="<password>" />
</properties>
</persistence-unit>
</persistence>
With sql logging enable, I get this output in my console
select nvarcharex0_.exampleField as col_0_0_ from NvarcharExample nvarcharex0_
SELECT e.exampleField FROM NvarcharExample e
I'm using
hibernate-core-4.3.10.Final.jar
hibernate-entitymanager-4.3.10.Final.jar
hibernate-jpa-2.1-api-1.0.0.Final.jar
hibernate-commons-annotations-4.0.5.Final.jar
sqljdbc41.jar
What I've tried:
using a varchar instead of nvarchar makes it work, but I need nvarchar
using jpql instead of sql works (see my example code), but I need a native query
I tried sqljdbc4.jar in Version 4.0 and 4.1 and I tried sqljdbc41.jar
I head about subclassing the SQL Server Dialect class, but did not have any success with that
I added <property name="dialect" value="org.hibernate.dialect.SQLServerDialect" /> to my persistence.xml (right behind the password property)
I added <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" /> to my persistence.xml
I changed the persistence provider to <provider>org.hibernate.ejb.HibernatePersistence</provider>
Using #Nationalized attribute helped me to map String to nvarchar for MS SQL 2012 without dialect subclassing.
At the same time setting the hibernate.use_nationalized_character_data property to true did not worked for me.
For futher information watch docs National Character Types.
I was able to resolve that issue by subclassing the SQLServerDialect:
package packagename;
import java.sql.Types;
public class SqlServerDialectWithNvarchar extends org.hibernate.dialect.SQLServerDialect {
public SqlServerDialectWithNvarchar() {
registerHibernateType(Types.NVARCHAR, 4000, "string");
}
}
and referencing it in my persistence.xml:
<property name="hibernate.dialect" value="packagename.SqlServerDialectWithNvarchar" />
PS: It seems to be fixed with hibernate 5.1 according to this ticket: https://hibernate.atlassian.net/browse/HHH-10183

Can one entity be persisted to multiple persistence units using OpenJPA?

I'm trying to save an entity to two separate persistence units one after the other. I can successfully save the entity to the first unit, I then detach it from that unit, reset the #Id value and persist to the second, but it appears that the object still has an associated id that is possibly not set-able? I think it's called the oid? The error:
Caused by: <openjpa-2.2.0-r422266:1244990 nonfatal store error>
org.apache.openjpa.persistence.EntityNotFoundException: The instance
of type "class za.co.core.ejb.entities.Address" with oid "4" no longer
exists in the data store. This may mean that you deleted the instance
in a separate transaction, but this context still has a cached version.
I know I can create a brand new object and copy the values I want across, but I want to do this generically without knowing too much about the object itself.
My code looks like this:
#PersistenceContext(unitName = "puOpenJPA_MSSQL",
type = PersistenceContextType.TRANSACTION)
private EntityManager entityManager;
#PersistenceContext(unitName = "puOpenJPA_MSSQLaudit",
type = PersistenceContextType.TRANSACTION)
private EntityManager auditManager;
...
entityManager.persist(entity);
entityManager.detach(entity);
entity.setId(null); //this sets the #id property of the entity to null
auditManager.persist(entity); //exception thrown
And here is the persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="puOpenJPA_MSSQL" transaction-type="JTA">
<provider>
org.apache.openjpa.persistence.PersistenceProviderImpl
</provider>
<jta-data-source>
java:jboss/datasources/mySqlSandbox
</jta-data-source>
<class>
za.co.core.ejb.entities.AuditableEntity
</class>
<class>za.co.core.ejb.entities.Address</class>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings"
value="buildSchema(ForeignKeys=true)" />
<property name="jboss.as.jpa.providerModule"
value="org.apache.openjpa" />
<property name="openjpa.DynamicEnhancementAgent"
value="false"/>
</properties>
</persistence-unit>
<persistence-unit name="puOpenJPA_MSSQLaudit" transaction-type="JTA">
<provider>
org.apache.openjpa.persistence.PersistenceProviderImpl
</provider>
<jta-data-source>
java:jboss/datasources/mySqlSandboxAudit
</jta-data-source>
<class>za.co.core.ejb.entities.AuditableEntity</class>
<class>za.co.core.ejb.entities.Address</class>
<properties>
<property name="openjpa.jdbc.SynchronizeMappings"
value="buildSchema(ForeignKeys=true)" />
<property name="jboss.as.jpa.providerModule"
value="org.apache.openjpa" />
<property name="openjpa.DynamicEnhancementAgent"
value="false" />
</properties>
</persistence-unit>
</persistence>
Thanks,
Sean
In theory yes because entities are "plain old java objects", but in practice to make all that magic work the persistence provider proxies parts of it, like collection members. As soon as you persist it, its not 'your' entity anymore - its part of the provider's bookkeeping.
If you want to persist the same entity multiple times, clone it multiple times and persist each individual copy.

Categories