android 上的 Ormlite TransactionManager 同步对我有用,但文档另有说明
Ormlite TransactionManager synchronization on android works for me but the documentation says otherwise
我希望在我的 Ormlite 模型上实现一些不完善的功能。在与服务器的同步过程中,我需要确保不会覆盖用户已编辑的行。为此,我需要一种方法来保证在同步线程读取脏位和更新值之间不允许用户编辑。
我正在使用单例 DatabaseHelper class:
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
private static DatabaseHelper databaseHelper = null;
private Dao<FormField, String> formFieldDao = null;
public static DatabaseHelper getHelper() {
if (databaseHelper == null) {
databaseHelper =
OpenHelperManager.getHelper(MyGoldCare.getAppContext(), DatabaseHelper.class);
}
return databaseHelper;
}
public Dao<FormField, String> getFormFieldDao() throws java.sql.SQLException {
if (formFieldDao == null) {
formFieldDao = getDao(FormField.class);
}
return formFieldDao;
}
我的第一个绝活是读取脏位并更新事务中的值。我写了一个简单的测试,看看这是否可以在没有竞争条件的情况下工作:
public class TestTransactionWriter implements Runnable {
FormField formField;
long sleepTime;
public TestTransactionWriter(FormField formField, long sleepTime){
this.formField = formField;
this.sleepTime = sleepTime;
}
@Override
public void run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
try {
TransactionManager.callInTransaction(DatabaseHelper.getHelper().getConnectionSource(), new Callable<Void>(){
public Void call() throws SQLException,InterruptedException{
for (int i = 0; i < 10; i++) {
DatabaseHelper.getHelper().getFormFieldDao().refresh(formField);
Thread.sleep(sleepTime);
Integer v = Integer.parseInt(formField.getValue());
v++;
formField.setValue(v.toString());
DatabaseHelper.getHelper().getFormFieldDao().update(formField);
}
return null;
}
});
}catch (SQLException se){
Log.e("TEST",se.toString());
}
}
}
然后我用上面的runnable启动了2个线程:
formField.setValue("0");
DatabaseHelper.getHelper().getFormFieldDao().update(formField);
TestTransactionWriter testTransactionWriter = new TestTransactionWriter(formField,1500);
Thread thread = new Thread(testTransactionWriter) ;
thread.start();
TestTransactionWriter testTransactionWriter1 = new TestTransactionWriter(formField,2000);
Thread thread1 = new Thread(testTransactionWriter) ;
thread1.start();
}catch (SQLException se){
Log.e(LOG_TAG,se.toString());
}
}
我得到了预期的结果。我每次测试最后的值都是20。当我删除交易中的电话时,我得到了竞争条件。我注意到的另一件事是,虽然这些线程是 运行,但我无法从数据库中读取任何值(也包括其他表),并且 UI 冻结了片刻。
为了确认我的测试,我决定通读 Ormlite 的源代码,我发现了以下警告:
* WARNING: it is up to you to properly synchronize around this method if multiple threads are using a
* connection-source which works gives out a single-connection. The reason why this is necessary is that multiple
* operations are performed on the connection and race-conditions will exist with multiple threads working on the
* same connection.
* </p>
*
* @param callable
* Callable to execute inside of the transaction.
* @return The object returned by the callable.
* @throws SQLException
* If the callable threw an exception then the transaction is rolled back and a SQLException wraps the
* callable exception and is thrown by this method.
*/
public <T> T callInTransaction(final Callable<T> callable) throws SQLException {
return callInTransaction(connectionSource, callable);
在我的例子中,即使我没有进行自己的同步,测试怎么也没有产生竞争条件?是不是一些 SQLlite 数据库锁启动了?考虑到我使用单例数据库助手的面孔,是否存在其他级别的同步?
How is it that in my case the test did not produce race conditions even though I did no synchronization of my own? Was it some SQLlite database lock kicking in?
不,但我怀疑一些 Sqlite 锁定启动了。该警告适用于所有 ORMLite 后端,包括 JDBC 允许多个线程连接到同一数据库。
For this purpose I need a way to guarantee that no user edit is allowed between the time of the sync thread reading the dirty bit and updating the values.
我会看一下 ORMLite 中的 version = true
支持,它在数据级别对此进行验证。它是为分布式系统制作的,在分布式系统中,多个客户端与数据库建立了多个连接,但它也可能对您有所帮助。见 version field documentation.
我希望在我的 Ormlite 模型上实现一些不完善的功能。在与服务器的同步过程中,我需要确保不会覆盖用户已编辑的行。为此,我需要一种方法来保证在同步线程读取脏位和更新值之间不允许用户编辑。
我正在使用单例 DatabaseHelper class:
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
private static DatabaseHelper databaseHelper = null;
private Dao<FormField, String> formFieldDao = null;
public static DatabaseHelper getHelper() {
if (databaseHelper == null) {
databaseHelper =
OpenHelperManager.getHelper(MyGoldCare.getAppContext(), DatabaseHelper.class);
}
return databaseHelper;
}
public Dao<FormField, String> getFormFieldDao() throws java.sql.SQLException {
if (formFieldDao == null) {
formFieldDao = getDao(FormField.class);
}
return formFieldDao;
}
我的第一个绝活是读取脏位并更新事务中的值。我写了一个简单的测试,看看这是否可以在没有竞争条件的情况下工作:
public class TestTransactionWriter implements Runnable {
FormField formField;
long sleepTime;
public TestTransactionWriter(FormField formField, long sleepTime){
this.formField = formField;
this.sleepTime = sleepTime;
}
@Override
public void run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
try {
TransactionManager.callInTransaction(DatabaseHelper.getHelper().getConnectionSource(), new Callable<Void>(){
public Void call() throws SQLException,InterruptedException{
for (int i = 0; i < 10; i++) {
DatabaseHelper.getHelper().getFormFieldDao().refresh(formField);
Thread.sleep(sleepTime);
Integer v = Integer.parseInt(formField.getValue());
v++;
formField.setValue(v.toString());
DatabaseHelper.getHelper().getFormFieldDao().update(formField);
}
return null;
}
});
}catch (SQLException se){
Log.e("TEST",se.toString());
}
}
}
然后我用上面的runnable启动了2个线程:
formField.setValue("0");
DatabaseHelper.getHelper().getFormFieldDao().update(formField);
TestTransactionWriter testTransactionWriter = new TestTransactionWriter(formField,1500);
Thread thread = new Thread(testTransactionWriter) ;
thread.start();
TestTransactionWriter testTransactionWriter1 = new TestTransactionWriter(formField,2000);
Thread thread1 = new Thread(testTransactionWriter) ;
thread1.start();
}catch (SQLException se){
Log.e(LOG_TAG,se.toString());
}
}
我得到了预期的结果。我每次测试最后的值都是20。当我删除交易中的电话时,我得到了竞争条件。我注意到的另一件事是,虽然这些线程是 运行,但我无法从数据库中读取任何值(也包括其他表),并且 UI 冻结了片刻。
为了确认我的测试,我决定通读 Ormlite 的源代码,我发现了以下警告:
* WARNING: it is up to you to properly synchronize around this method if multiple threads are using a
* connection-source which works gives out a single-connection. The reason why this is necessary is that multiple
* operations are performed on the connection and race-conditions will exist with multiple threads working on the
* same connection.
* </p>
*
* @param callable
* Callable to execute inside of the transaction.
* @return The object returned by the callable.
* @throws SQLException
* If the callable threw an exception then the transaction is rolled back and a SQLException wraps the
* callable exception and is thrown by this method.
*/
public <T> T callInTransaction(final Callable<T> callable) throws SQLException {
return callInTransaction(connectionSource, callable);
在我的例子中,即使我没有进行自己的同步,测试怎么也没有产生竞争条件?是不是一些 SQLlite 数据库锁启动了?考虑到我使用单例数据库助手的面孔,是否存在其他级别的同步?
How is it that in my case the test did not produce race conditions even though I did no synchronization of my own? Was it some SQLlite database lock kicking in?
不,但我怀疑一些 Sqlite 锁定启动了。该警告适用于所有 ORMLite 后端,包括 JDBC 允许多个线程连接到同一数据库。
For this purpose I need a way to guarantee that no user edit is allowed between the time of the sync thread reading the dirty bit and updating the values.
我会看一下 ORMLite 中的 version = true
支持,它在数据级别对此进行验证。它是为分布式系统制作的,在分布式系统中,多个客户端与数据库建立了多个连接,但它也可能对您有所帮助。见 version field documentation.