Set Isolation level in eclipselink - jpa

I would like to set isolation level using eclipse link,
I tried these 2 ways to do it:
java.sql.Connection
mgr = EMF.get().createEntityManager();
tx = mgr.getTransaction();
tx.begin();
java.sql.Connection connection = mgr.unwrap(java.sql.Connection.class);
connection.setTransactionIsolation(java.sql.Connection.TRANSACTION_READ_COMMITTED);
System.out.println("Connection: "+connection.getTransactionIsolation());
//prints TRANSACTION_READ_COMMITED as expected
org.eclipse.persistence.sessions.DatabaseLogin databaseLogin = new DatabaseLogin();
System.out.println("DatabaseLoging: "+databaseLogin.getTransactionIsolation());
//prints -1, representing transaction isolation is not set
DatabaseLogin setTransationIsolation method
mgr = EMF.get().createEntityManager();
tx = mgr.getTransaction();
tx.begin();
org.eclipse.persistence.sessions.DatabaseLogin databaseLogin = new DatabaseLogin();
databaseLogin.setTransactionIsolation(DatabaseLogin.TRANSACTION_READ_COMMITTED);
System.out.println("DatabaseLoging: "+databaseLogin.getTransactionIsolation());
//prints TRANSACTION_READ_COMMITED as expected
java.sql.Connection connection = mgr.unwrap(java.sql.Connection.class);
System.out.println("Connection: "+connection.getTransactionIsolation());
//prints TRANSACTION_REPEATABLE_READ
As you can see there are some inconsistencies between the return values of getTransacationIsolation() method. My question is, which transaction isolation is really set in both cases ? I know that eclipse link uses different connection for read and write operations by default, DatabaseLogin.setTransactionIsolation should set the isolation level for both connections, so why Connection.getTransactionIsolation still returns another isolation level ?
I am using Application scoped EntityManager, JPA 2.0, EclipseLink 2.5.2.
If there are more preferable ways setting the transaction isolation, please let me know.

After having a small pause with eclipse link, I finally found out how to set transaction isolation level.
As #Chris correctly mentioned in his answer I need to obtain DatabaseLogin used by sessions. After a small research on eclipse link sessions I have found out, that I can change the Session properties in my own SessionCustomizer, see the code below:
package com.filip.blabla;
import org.eclipse.persistence.sessions.DatabaseLogin;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.factories.SessionCustomizer;
public class DFSessionCustomizer implements SessionCustomizer {
#Override
public void customize(Session session) throws Exception {
DatabaseLogin databaseLogin = (DatabaseLogin) session.getDatasourceLogin();
databaseLogin.setTransactionIsolation(DatabaseLogin.TRANSACTION_READ_COMMITTED);
}
}
set SessionCustomizer in persistence.xml
<property name="eclipselink.session.customizer" value="com.filip.blabla.DFSessionCustomizer"/>

The databaseLogin class is an internal object that EclipseLink uses to configure how it accesses the database, and the settings used to configure those connections. Any changes you make directly to a connection will not be reflected in a DatabaseLogin instance.
Just creating a new DatabaseLoging instance is not going to give you access to the settings being used by the persistence unit. You need to obtain the DatabaseLogin being used by the sessions underneath the the EntityManager/EMF.

Related

How to implement master/slave structure with squeryl and play framework

I am running a play framework website that uses squeryl and mysql database.
I need to use squeryl to run all read queries to the slave and all write queries to the master.
How can I achieve this? either via squeryl or via jdbc connector itself.
Many thanks,
I don't tend to use MySQL myself, but here's an idea:
Based on the documentation here, the MySQL JDBC driver will round robin amongst the slaves if the readOnly attribute is properly set on the Connnection. In order to retrieve and change the current Connection you'll want to use code like
transaction {
val conn = Session.currentSession.connection
conn.setReadOnly(true)
//Your code here
}
Even better, you can create your own readOnlyTransaction method:
def readOnlyTransaction(f: => Unit) = {
transaction {
val conn = Session.currentSession.connection
val orig = conn.getReadOnly()
conn.setReadOnly(true)
f
conn.setReadOnly(orig)
}
}
Then use it like:
readOnlyTransaction {
//Your code here
}
You'll probably want to clean that up a bit so the default readOnly state is reset if an exception occurs, but you get the general idea.

Transaction Scope Isolation level and EF6

I am testing the EF6 isolation level but the test fails with:
Assert.AreEqual failed. Expected:<ReadUncommitted>. Actual:<Unspecified>.
The test:
public void TestIsolationLevelReadUncommitted()
{
// Arrange
using (
new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions {IsolationLevel = IsolationLevel.ReadUncommitted}))
{
using (var context = new BoligEntities())
{
// Act
context.GetDbConnection().Open();
var isolationLevel = context.GetDbConnection().GetIsolationLevel();
// Assert
Assert.AreEqual(System.Data.IsolationLevel.ReadUncommitted, isolationLevel);
}
}
}
The test doesn't make much sense but I am wondering why it fails.
there is plenty of posts around transaction scope and EF.
Actually add uncommitted read and nolock to your searches.
Good basic explanation and example
FROM EF 6 on...
EF transaction scope docu
Generally you dont need it. ( there are exceptions)
and
i hope i dont have to support the system that uses uncommitted reads. ;-) Filthy...
good luck
Transaction is not the same as database. Looks like you are checking Isolation level for open connection but not for running transaction.
Generally speaking - you can open connection and run multiple transaction on this connection with different Isolation level.
using (var context = new MyEntities())
{
using (var tran = context.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted))
{
Assert.AreEqual(System.Data.IsolationLevel.ReadUncommitted, tran.UnderlyingTransaction.IsolationLevel);

Entity Framework DefaultConnectionFactory being ignored

I'm using Entity Framework 5 with Code First. I've written a custom IDbConnectionFactory which I want to use for all the connections made by my DbContext class, so early on in the application's lifecycle, before any database work is done, I call
Database.DefaultConnectionFactory = new MyConnectionFactory();
However, MyConnectionFactory.CreateConnection is never called, which suggests to me that EF's changed it back - but the debugger shows that it's still a MyConnectionFactory after several queries have run. For some reason, it's just not using it.
My DbContext is initialised by passing the name of a connection string from the app.config file, and those connection strings do specify an explicit provider (as indeed they have to) so I'm wondering if that's causing a per-connection override of the connection factory based on the connection string. Does this happen and can I stop it without registering a completely new provider (although maybe that's not too hard to do?).
Whatever I see online about this (much obscured by the defaultConnectionFactory tag in various app.config examples) suggests you can just change it to an IDbConnectionFactory instance of your choice and it'll work, but mine isn't behaving.
The purpose of this is to allow me to run a particular set of SQL statements whenever a new connection is opened, so the second part of this question would be does anybody know a better way to do this?
I know it is not ideal but this worked for me:
public class DBBase : DbContext
{
public DBBase(string nameOrConnectionString)
: base(Database.DefaultConnectionFactory.CreateConnection(nameOrConnectionString), true)
{
}
// ...
}
You need to get the connection that you built for each call that you are wanting to use. For example using the following code.
private static void UsingCustomConnection()
{
using (var conn = Database.DefaultConnectionFactory.CreateConnection("YourDbName"))
{
using (var context = new YourContext(conn))
{
context.Destinations.Add(new Destination {Name = "Colorado"});
context.SaveChanges();
}
}
}
You will need to setup this in YourContext
public YourContext(DbConnection connection)
: base(connection, contextOwnsConnection: false)
{
}

Manage Transactions on Business Layer

I want to use TransactionScope class in my business layer to manage database operation in data access layer.
Here is my sample code. When i execute it, it tries to enable the dtc. I want to do the operation without enable dtc.
I already checked https://entlib.codeplex.com/discussions/32592 article. It didn't work for me. I read many articles on this subject but none of them really touch enterprise library or i didn't see.
by the way, i am able to use TransactionScope using dotnet sql client and it works pretty well.
what would be the inside of SampleInsert() method?
Thanks,
Business Layer method:
public void SampleInsert()
{
using (TransactionScope scope = new TransactionScope())
{
Sample1DAL dal1 = new Sample1DAL(null);
Sample2DAL dal2 = new Sample2DAL(null);
Sample3DAL dal3 = new Sample3DAL(null);
dal1.SampleInsert();
dal2.SampleInsert();
dal3.SampleInsert();
scope.Complete();
}
}
Data Access Layer method:
//sampleInsert method structurally same for each 3 dal
public void SampleInsert()
{
Database database = DatabaseFactory.CreateDatabase(Utility.DATABASE_INFO); ;
using (DbConnection conn = database.CreateConnection())
{
conn.Open();
DbCommand cmd = database.GetStoredProcCommand("P_TEST_INS", "some value3");
database.ExecuteNonQuery(cmd);
}
}
Hi yes this will enable dtc because you are creating 3 DB connections within one TransactionScope . When more than one DB connection is created within same TransactionScope the local transaction escalate to Distributed Transaction and hence dtc will be enabled to manage Distributed Trnsactions.You will have to do it in a way that only one DB connection is created for entire TransactionScope. I hope this will give you an idea.
After research and waching query analyzer, I changed the SampleInsert() body as follows and it worked. The problem was as ethicallogics mentioned opening new connection each time i access the database.
public void SampleInsert()
{
Database database = DatabaseFactory.CreateDatabase(Utility.DATABASE_INFO);
using (DbCommand cmd = database.GetStoredProcCommand("P_TEST_INS", "some value1"))
{
database.ExecuteNonQuery(cmd);
}
}

How to add c3p0 or DBCP Connection pool?

Could you please explain to me how to add a standalone c3pO or DBCP connection pool to my toplink-based JPA project?
I have a persistence.xml file, and everytime I want to query the database, I'm doing this:
EntityManagerFactory emf = this.getEntityManagerFactory();
// Surely using persistence.xml to set up the factory
EntityManager em = emf.createEntityManager();
...
Where do I build the bridge between my external connection pool manager and Toplink? Any ideas or links are welcomed.
Regards,
Jean
I don't use Toplink so I didn't test this but, my understanding of various resources found over the net is that you'll need to provide an implementation of SessionCustomizer. In this implementation, use the JNDIConnector class to give a DataSource object (c3p0 implements the DataSource API) using the setDataSource(javax.sql.DataSource) method.
Adapt the sample from Working with a non JTA DataSource in Toplink Essentials.
I really don't understand what to do. So blurry page for a beginner. Nevertheless, I have created a SessionCustomizer class apart. Here is my customize() method, using c3p0:
public void customize(Session session) throws Exception{
DataSource ds = DataSources.unpooledDataSource("myServerURL", "login", "pwd");
DataSource pooled = DataSources.pooledDataSource(ds);
JNDIConnector conn = (JNDIConnector)session.getLogin().getConnector();
conn.setDataSource(pooled);
conn.setLookupType(JNDIConnector.STRING_LOOKUP);
}
I don't even think it's correct. I'm putting my connection infos in clear in code, really weird.
Secondly, in persistence.xml example from the link, they have put:
<non-jta-data-source>java:comp/env/jdbc/DefaultDS</non-jta-data-source>
<class>sample.MyEntity</class>
<properties>
<property name="toplink.session.customizer" value="es.claro.commons.ds.DataSourceSessionCustomizer"/>
</properties>
What should I put in mine, particularly for "non-jta-data-source" tag? Is there a way to put connection informations in that xml instead of in code?
Help.