I was recently learning Room Database but this code seems too weird.
I tried to understand this line of code but maybe my google search skill isn't that good.
Can you explain this?
public abstract NoDoDao noDoDao();
The thing I don't understand, how Interface which is 'NoDoDa' here, can be called as if it were 'method' or 'function'? This is my first time seeing this syntax. And why is there also 'Abstract keyword'?
package com.paige.room.data;
import android.content.Context;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.sqlite.db.SupportSQLiteDatabase;
import com.paige.room.model.NoDo;
#Database(entities = {NoDo.class}, version = 1)
public abstract class NoDoRoomDatabase extends RoomDatabase {
//Don't want to create a lot of instances, singleton
//volatile means "don't store in the cache"
private static volatile NoDoRoomDatabase INSTANCE;
public abstract NoDoDao noDoDao();
public static NoDoRoomDatabase getDatabase(final Context context){
if(INSTANCE == null){
//synchronized keyword, for UI Thread, force it to work as it is supposed to do
synchronized (NoDoRoomDatabase.class){
if(INSTANCE == null){
//create our db
INSTANCE = Room.databaseBuilder(
context,
NoDoRoomDatabase.class,
"nodo_database")
.addCallback(roomDatabaseCallBack)
.build();
}
}
}
return INSTANCE;
}
//callback
private static RoomDatabase.Callback roomDatabaseCallBack =
new RoomDatabase.Callback(){
#Override
public void onOpen(#NonNull SupportSQLiteDatabase db) {
super.onOpen(db);
new PopulateDBAsync(INSTANCE).execute();
}
};
private static class PopulateDBAsync extends AsyncTask<Void, Void, Void> {
private final NoDoDao noDoDao;
PopulateDBAsync(NoDoRoomDatabase db) {
this.noDoDao = db.noDoDao();
}
#Override
protected Void doInBackground(Void... voids) {
// noDoDao.deleteAll(); //removes all items from our table
// //for testing
// NoDo noDo = new NoDo("Buy a new Ferrari");
// noDoDao.insert(noDo);
//
// noDo = new NoDo("Buy a Big House");
// noDoDao.insert(noDo);
return null;
}
}
}
This is my entire code
NoDoDao is an abstract class, it cannot be instantiated (constructed) but the class can be used. Which is what is happening here.
What you may not be aware of is that Room generates code and it is these abstractions that allow the underlying code to be implemented on your behalf.
Say for example that you Entity class NoDO is :-
#Entity
public class NoDo {
#PrimaryKey
Long id;
}
And your Dao class NoDoDao is :-
#Dao
public abstract class NoDoDao {
#Query("SELECT * FROM nodo")
abstract List<NoDo> getnothing();
}
And you compile (F9) then you get :-
Looking at NoDoDao_Impl then you get :-
public final class NoDoDao_Impl extends NoDoDao {
private final RoomDatabase __db;
public NoDoDao_Impl(RoomDatabase __db) {
this.__db = __db;
}
#Override
List<NoDo> getnothing() {
final String _sql = "SELECT * FROM nodo";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
__db.assertNotSuspendingTransaction();
final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
final List<NoDo> _result = new ArrayList<NoDo>(_cursor.getCount());
while(_cursor.moveToNext()) {
final NoDo _item;
_item = new NoDo();
if (_cursor.isNull(_cursorIndexOfId)) {
_item.id = null;
} else {
_item.id = _cursor.getLong(_cursorIndexOfId);
}
_result.add(_item);
}
return _result;
} finally {
_cursor.close();
_statement.release();
}
}
}
That is the real getNothing code has been generated by the annotation processor.
As important is that the NoDoRoomDatabase_Impl is :-
public final class NoDoRoomDatabase_Impl extends NoDoRoomDatabase {
private volatile NoDoDao _noDoDao;
#Override
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1) {
#Override
public void createAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("CREATE TABLE IF NOT EXISTS `NoDo` (`id` INTEGER, PRIMARY KEY(`id`))");
_db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
_db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1fdde93d9c60610ae88bf0c74771844a')");
}
#Override
public void dropAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("DROP TABLE IF EXISTS `NoDo`");
if (mCallbacks != null) {
for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
mCallbacks.get(_i).onDestructiveMigration(_db);
}
}
}
#Override
protected void onCreate(SupportSQLiteDatabase _db) {
if (mCallbacks != null) {
for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
mCallbacks.get(_i).onCreate(_db);
}
}
}
#Override
public void onOpen(SupportSQLiteDatabase _db) {
mDatabase = _db;
internalInitInvalidationTracker(_db);
if (mCallbacks != null) {
for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
mCallbacks.get(_i).onOpen(_db);
}
}
}
#Override
public void onPreMigrate(SupportSQLiteDatabase _db) {
DBUtil.dropFtsSyncTriggers(_db);
}
#Override
public void onPostMigrate(SupportSQLiteDatabase _db) {
}
#Override
protected RoomOpenHelper.ValidationResult onValidateSchema(SupportSQLiteDatabase _db) {
final HashMap<String, TableInfo.Column> _columnsNoDo = new HashMap<String, TableInfo.Column>(1);
_columnsNoDo.put("id", new TableInfo.Column("id", "INTEGER", false, 1, null, TableInfo.CREATED_FROM_ENTITY));
final HashSet<TableInfo.ForeignKey> _foreignKeysNoDo = new HashSet<TableInfo.ForeignKey>(0);
final HashSet<TableInfo.Index> _indicesNoDo = new HashSet<TableInfo.Index>(0);
final TableInfo _infoNoDo = new TableInfo("NoDo", _columnsNoDo, _foreignKeysNoDo, _indicesNoDo);
final TableInfo _existingNoDo = TableInfo.read(_db, "NoDo");
if (! _infoNoDo.equals(_existingNoDo)) {
return new RoomOpenHelper.ValidationResult(false, "NoDo(a.so59478461.NoDo).\n"
+ " Expected:\n" + _infoNoDo + "\n"
+ " Found:\n" + _existingNoDo);
}
return new RoomOpenHelper.ValidationResult(true, null);
}
}, "1fdde93d9c60610ae88bf0c74771844a", "1d145662ebde2538a8da3217996c0550");
final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
.name(configuration.name)
.callback(_openCallback)
.build();
final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
return _helper;
}
#Override
protected InvalidationTracker createInvalidationTracker() {
final HashMap<String, String> _shadowTablesMap = new HashMap<String, String>(0);
HashMap<String, Set<String>> _viewTables = new HashMap<String, Set<String>>(0);
return new InvalidationTracker(this, _shadowTablesMap, _viewTables, "NoDo");
}
#Override
public void clearAllTables() {
super.assertNotMainThread();
final SupportSQLiteDatabase _db = super.getOpenHelper().getWritableDatabase();
try {
super.beginTransaction();
_db.execSQL("DELETE FROM `NoDo`");
super.setTransactionSuccessful();
} finally {
super.endTransaction();
_db.query("PRAGMA wal_checkpoint(FULL)").close();
if (!_db.inTransaction()) {
_db.execSQL("VACUUM");
}
}
}
#Override
public NoDoDao noDoDao() {
if (_noDoDao != null) {
return _noDoDao;
} else {
synchronized(this) {
if(_noDoDao == null) {
_noDoDao = new NoDoDao_Impl(this);
}
return _noDoDao;
}
}
}
}
See how NoDoDao has been overridden to basically be NoDoDao_Impl
Related
So I'm trying to use Executor execute the Dao's in my android app, but when I created the Executor, it said it required the method being run to have a Runnable return type?
So I made them all return Runnable, but now Android Studio gives me the error:
/database/TripDao.java:28: error: Not sure how to handle insert method's return type.
Runnable insertTrip(Trip trip);
^
Dao:
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;
import java.util.UUID;
#Dao
public interface TripDao {
#Query("DELETE FROM trip")
public void deleteTrips();
#Query("SELECT * FROM trip")
LiveData<List<Trip>> getAll();
#Query("SELECT * FROM trip WHERE uuid=:id")
public LiveData<Trip> getTrip(UUID id);
#Delete
Runnable delete(Trip trip);
#Insert
Runnable insertTrip(Trip trip);
#Update
Runnable updateTrip(Trip trip);
}
Repository:
package com.example.csc202assignment.database;
import android.content.Context;
import androidx.lifecycle.LiveData;
import androidx.room.Room;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class TripRepository {
private static TripRepository INSTANCE = null;
private final String DATABASE_NAME = "trip-database";
private Context context;
TripDatabase database;
static TripDao tripDao;
Executor executor = Executors.newSingleThreadExecutor();
private TripRepository(Context context) {
this.context = context;
this.database = Room.databaseBuilder(
context.getApplicationContext(),
TripDatabase.class,DATABASE_NAME).build();
tripDao = database.tripDao();
}
void deleteTrips(){
tripDao.deleteTrips();
}
void delete(Trip trip){
executor.execute(tripDao.delete(trip));
}
void insertTrip(Trip trip){
executor.execute(tripDao.insertTrip(trip));
}
void updateTrip(Trip trip){
executor.execute(tripDao.updateTrip(trip));
}
public LiveData<List<Trip>> getAll(){ return tripDao.getAll();
}
public static LiveData<Trip> getTrip(UUID id){
return tripDao.getTrip(id);
}
public static void initialise(Context context){
if(INSTANCE==null){
INSTANCE = new TripRepository(context);
}
}
public static TripRepository get() throws IllegalStateException {
try {
return INSTANCE;
} catch (IllegalStateException e) {
System.out.println("TripRepository must be initialised");
}
return INSTANCE;
}
}
Dao_Impl
package com.example.csc202assignment.database;
import android.database.Cursor;
import androidx.lifecycle.LiveData;
import androidx.room.EntityDeletionOrUpdateAdapter;
import androidx.room.EntityInsertionAdapter;
import androidx.room.RoomDatabase;
import androidx.room.RoomSQLiteQuery;
import androidx.room.SharedSQLiteStatement;
import androidx.room.util.CursorUtil;
import androidx.room.util.DBUtil;
import androidx.sqlite.db.SupportSQLiteStatement;
import java.lang.Class;
import java.lang.Exception;
import java.lang.Override;
import java.lang.Runnable;
import java.lang.String;
import java.lang.SuppressWarnings;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
#SuppressWarnings({"unchecked", "deprecation"})
public final class TripDao_Impl implements TripDao {
private final RoomDatabase __db;
private final EntityInsertionAdapter<Trip> __insertionAdapterOfTrip;
private final TripTypeConverter __tripTypeConverter = new TripTypeConverter();
private final EntityDeletionOrUpdateAdapter<Trip> __deletionAdapterOfTrip;
private final EntityDeletionOrUpdateAdapter<Trip> __updateAdapterOfTrip;
private final SharedSQLiteStatement __preparedStmtOfDeleteTrips;
public TripDao_Impl(RoomDatabase __db) {
this.__db = __db;
this.__insertionAdapterOfTrip = new EntityInsertionAdapter<Trip>(__db) {
#Override
public String createQuery() {
return "INSERT OR ABORT INTO `Trip` (`uuid`,`title`,`destination`,`date`,`duration`) VALUES (?,?,?,?,?)";
}
#Override
public void bind(SupportSQLiteStatement stmt, Trip value) {
final String _tmp;
_tmp = __tripTypeConverter.fromUUID(value.uuid);
if (_tmp == null) {
stmt.bindNull(1);
} else {
stmt.bindString(1, _tmp);
}
if (value.title == null) {
stmt.bindNull(2);
} else {
stmt.bindString(2, value.title);
}
if (value.destination == null) {
stmt.bindNull(3);
} else {
stmt.bindString(3, value.destination);
}
if (value.date == null) {
stmt.bindNull(4);
} else {
stmt.bindString(4, value.date);
}
if (value.duration == null) {
stmt.bindNull(5);
} else {
stmt.bindString(5, value.duration);
}
}
};
this.__deletionAdapterOfTrip = new EntityDeletionOrUpdateAdapter<Trip>(__db) {
#Override
public String createQuery() {
return "DELETE FROM `Trip` WHERE `uuid` = ?";
}
#Override
public void bind(SupportSQLiteStatement stmt, Trip value) {
final String _tmp;
_tmp = __tripTypeConverter.fromUUID(value.uuid);
if (_tmp == null) {
stmt.bindNull(1);
} else {
stmt.bindString(1, _tmp);
}
}
};
this.__updateAdapterOfTrip = new EntityDeletionOrUpdateAdapter<Trip>(__db) {
#Override
public String createQuery() {
return "UPDATE OR ABORT `Trip` SET `uuid` = ?,`title` = ?,`destination` = ?,`date` = ?,`duration` = ? WHERE `uuid` = ?";
}
#Override
public void bind(SupportSQLiteStatement stmt, Trip value) {
final String _tmp;
_tmp = __tripTypeConverter.fromUUID(value.uuid);
if (_tmp == null) {
stmt.bindNull(1);
} else {
stmt.bindString(1, _tmp);
}
if (value.title == null) {
stmt.bindNull(2);
} else {
stmt.bindString(2, value.title);
}
if (value.destination == null) {
stmt.bindNull(3);
} else {
stmt.bindString(3, value.destination);
}
if (value.date == null) {
stmt.bindNull(4);
} else {
stmt.bindString(4, value.date);
}
if (value.duration == null) {
stmt.bindNull(5);
} else {
stmt.bindString(5, value.duration);
}
final String _tmp_1;
_tmp_1 = __tripTypeConverter.fromUUID(value.uuid);
if (_tmp_1 == null) {
stmt.bindNull(6);
} else {
stmt.bindString(6, _tmp_1);
}
}
};
this.__preparedStmtOfDeleteTrips = new SharedSQLiteStatement(__db) {
#Override
public String createQuery() {
final String _query = "DELETE FROM trip";
return _query;
}
};
}
#Override
public Runnable insertTrip(final Trip trip) {
__db.assertNotSuspendingTransaction();
}
#Override
public Runnable delete(final Trip trip) {
__db.assertNotSuspendingTransaction();
}
#Override
public Runnable updateTrip(final Trip trip) {
__db.assertNotSuspendingTransaction();
}
#Override
public void deleteTrips() {
__db.assertNotSuspendingTransaction();
final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteTrips.acquire();
__db.beginTransaction();
try {
_stmt.executeUpdateDelete();
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
__preparedStmtOfDeleteTrips.release(_stmt);
}
}
#Override
public LiveData<List<Trip>> getAll() {
final String _sql = "SELECT * FROM trip";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
return __db.getInvalidationTracker().createLiveData(new String[]{"trip"}, false, new Callable<List<Trip>>() {
#Override
public List<Trip> call() throws Exception {
final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfUuid = CursorUtil.getColumnIndexOrThrow(_cursor, "uuid");
final int _cursorIndexOfTitle = CursorUtil.getColumnIndexOrThrow(_cursor, "title");
final int _cursorIndexOfDestination = CursorUtil.getColumnIndexOrThrow(_cursor, "destination");
final int _cursorIndexOfDate = CursorUtil.getColumnIndexOrThrow(_cursor, "date");
final int _cursorIndexOfDuration = CursorUtil.getColumnIndexOrThrow(_cursor, "duration");
final List<Trip> _result = new ArrayList<Trip>(_cursor.getCount());
while(_cursor.moveToNext()) {
final Trip _item;
_item = new Trip();
final String _tmp;
if (_cursor.isNull(_cursorIndexOfUuid)) {
_tmp = null;
} else {
_tmp = _cursor.getString(_cursorIndexOfUuid);
}
_item.uuid = __tripTypeConverter.toUUID(_tmp);
if (_cursor.isNull(_cursorIndexOfTitle)) {
_item.title = null;
} else {
_item.title = _cursor.getString(_cursorIndexOfTitle);
}
if (_cursor.isNull(_cursorIndexOfDestination)) {
_item.destination = null;
} else {
_item.destination = _cursor.getString(_cursorIndexOfDestination);
}
if (_cursor.isNull(_cursorIndexOfDate)) {
_item.date = null;
} else {
_item.date = _cursor.getString(_cursorIndexOfDate);
}
if (_cursor.isNull(_cursorIndexOfDuration)) {
_item.duration = null;
} else {
_item.duration = _cursor.getString(_cursorIndexOfDuration);
}
_result.add(_item);
}
return _result;
} finally {
_cursor.close();
}
}
#Override
protected void finalize() {
_statement.release();
}
});
}
#Override
public LiveData<Trip> getTrip(final UUID id) {
final String _sql = "SELECT * FROM trip WHERE uuid=?";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
int _argIndex = 1;
final String _tmp;
_tmp = __tripTypeConverter.fromUUID(id);
if (_tmp == null) {
_statement.bindNull(_argIndex);
} else {
_statement.bindString(_argIndex, _tmp);
}
return __db.getInvalidationTracker().createLiveData(new String[]{"trip"}, false, new Callable<Trip>() {
#Override
public Trip call() throws Exception {
final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfUuid = CursorUtil.getColumnIndexOrThrow(_cursor, "uuid");
final int _cursorIndexOfTitle = CursorUtil.getColumnIndexOrThrow(_cursor, "title");
final int _cursorIndexOfDestination = CursorUtil.getColumnIndexOrThrow(_cursor, "destination");
final int _cursorIndexOfDate = CursorUtil.getColumnIndexOrThrow(_cursor, "date");
final int _cursorIndexOfDuration = CursorUtil.getColumnIndexOrThrow(_cursor, "duration");
final Trip _result;
if(_cursor.moveToFirst()) {
_result = new Trip();
final String _tmp_1;
if (_cursor.isNull(_cursorIndexOfUuid)) {
_tmp_1 = null;
} else {
_tmp_1 = _cursor.getString(_cursorIndexOfUuid);
}
_result.uuid = __tripTypeConverter.toUUID(_tmp_1);
if (_cursor.isNull(_cursorIndexOfTitle)) {
_result.title = null;
} else {
_result.title = _cursor.getString(_cursorIndexOfTitle);
}
if (_cursor.isNull(_cursorIndexOfDestination)) {
_result.destination = null;
} else {
_result.destination = _cursor.getString(_cursorIndexOfDestination);
}
if (_cursor.isNull(_cursorIndexOfDate)) {
_result.date = null;
} else {
_result.date = _cursor.getString(_cursorIndexOfDate);
}
if (_cursor.isNull(_cursorIndexOfDuration)) {
_result.duration = null;
} else {
_result.duration = _cursor.getString(_cursorIndexOfDuration);
}
} else {
_result = null;
}
return _result;
} finally {
_cursor.close();
}
}
#Override
protected void finalize() {
_statement.release();
}
});
}
public static List<Class<?>> getRequiredConverters() {
return Collections.emptyList();
}
}
I've been told I need to use Executor for INSERT, UPDATE and DELETE so they don't get block the main thread, so I'm not sure how to fix this without just removing the executors.
As far as I can figure, there's no actual Runnable object to return, so it doesn't know what to do? If I've got that wrong please correct me.
But yeah, I just can't figure out how to get around the executor wanting a Runnable return type, but there being no Runnable to return. Any help would be appreciated
You are trying to tell room to return a Runnable from the insert. Room knows that an #Insert can only return void or Long/long (the rowid of the inserted row).
if the #Insert inserts multiple rows then it can return void, long[] or Long[].
if you were look at the Build log, then the
You can't have :-
#Insert
Runnable insertTrip(Trip trip);
It must be one of the following:-
#Insert
void insertTrip(Trip trip);
or
#Insert
long insertTrip(Trip trip);
or
#Insert
Long insertTrip(Trip trip);
Similar for #Update and #Delete except they return int (the number or affected rows)
To get/use the Runnable you could use :-
void insertTrip(Trip trip){
executor.execute(new Runnable() {
#Override
public void run() {
tripDao.insertTrip(trip);
}
});
}
obviously likewise for the others.
Working example
Using (note with a simple Trip class but all other classes as per your question bar the changes above) :-
TripRepository.initialise(this);
Trip atrip = new Trip();
atrip.tripName = "My Trip";
TripRepository.get().insertTrip(atrip);
Results in :-
private static final Map<Integer, GameObject> OBJECT = new ConcurrentHashMap<>();
I have a map in which I store GameObjects, which is extended by PlayerObject, NpcObject, ItemObject.
I'm trying to create a method on which I call the object by ID and class type and cast at it directly and if it's not exists or the class of the object ID does not match the given one to return null.
So for example
final PlayerObject object = getObject(<id>, PlayerObject);
Is there any way?
Edit:
I managed to do this:
public <T extends EventObject> T getObject(final int objectId)
{
final EventObject object = OBJECT.get(objectId);
return Objects.nonNull(object) && object.getClass() == ? T (object) : null;
}
But i don't want to use Class<? extends EventObject> in parameter of this method. Can't i somehow check using the generic T if it's the same class to cast it and return or else null?
You can use Class#isInstance to check if the object's type is correct and Class#cast to convert the object to the correct type.
public static <T extends GameObject> T getObject(Integer id, Class<T> clazz) {
GameObject obj = OBJECT.get(id);
if(!clazz.isInstance(obj)) return null;
return clazz.cast(obj);
}
// ...
final PlayerObject object = getObject(<id>, PlayerObject.class);
try this complete generic method:
public static <T> T getObject(int id, Class<T> c){
Object object = OBJECT.get(id);
return object != null && object.getClass() == c ? c.cast(object) : null;
}
The other parts of the program:
private static final Map<Integer, GameObject> OBJECT = new ConcurrentHashMap<>();
public static void main(String[] args) throws ParseException {
init();
PlayerObject p = getObject(3, PlayerObject.class);
ItemObject i = getObject(3, ItemObject.class);
PlayerObject p2 = getObject(4, PlayerObject.class);
System.out.println(p);
System.out.println(i);
System.out.println(p2);
}
private static void init() {
OBJECT.put(1, new PlayerObject(1, "SomePlayer1"));
OBJECT.put(2, new PlayerObject(2, "SomePlayer2"));
OBJECT.put(3, new ItemObject(3, 5));
OBJECT.put(4, new ItemObject(4, 7));
}
GameObject.class
public class GameObject {
protected int id;
GameObject(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
}
PlayerObject.class
public class PlayerObject extends GameObject {
private String playerName;
PlayerObject(int id, String playerName) {
super(id);
this.playerName = playerName;
}
public String getPlayerName() {
return this.playerName;
}
public void setPlayerName(String playerName) {
this.playerName = playerName;
}
#Override
public String toString() {
return "PlayerObject{\"id\": " +
this.id +
", \"playerName\": \"" +
this.playerName +
"\"}";
}
}
ItemObject.class
public class ItemObject extends GameObject {
private int itemCount;
ItemObject(int id, int itemCount) {
super(id);
this.itemCount = itemCount;
}
public int getItemCount() {
return this.itemCount;
}
public void setItemCount(int itemCount) {
this.itemCount = itemCount;
}
#Override
public String toString() {
return "ItemObject{\"id\": " +
this.id +
", \"itemCount\": " +
this.itemCount +
"}";
}
}
And the output of the program:
PlayerObject{"id": 1, "playerName": "SomePlayer1"}
ItemObject{"id": 3, "itemCount": 5}
null
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
public class Stackoverflow_68734414 {
private static Map<Integer, GameObject> OBJECT_MAP = new ConcurrentHashMap<>();
public static void main(String[] args) {
PlayerObject po = new PlayerObject();
ItemObject io = new ItemObject();
OBJECT_MAP.put(1, po);
OBJECT_MAP.put(2, io);
PlayerObject p1 = getObject(1, PlayerObject.class);
PlayerObject p2 = getObject(2, PlayerObject.class);
ItemObject i1 = getObject(1, ItemObject.class);
ItemObject i2 = getObject(2, ItemObject.class);
System.out.println(p1);
System.out.println(p2);
System.out.println(i1);
System.out.println(i2);
}
public static <T extends GameObject> T getObject(Integer id, Class<T> klass){
GameObject object = OBJECT_MAP.get(id);
if(Objects.nonNull(object) && (object.getClass() == klass)) {
return klass.cast(object);
} else{
return null;
}
}
}
class GameObject{
}
class PlayerObject extends GameObject {
}
class ItemObject extends GameObject{
}
Output is as expected:
PlayerObject#179d3b25
null
null
ItemObject#254989ff
Are you looking for something similar to this:
public boolean isItemObject(int id){
GameObject obj = OBJECT.get(id)
if(obj instanceof ItemObject && obj != null){
return true;
}
return false;
}
public boolean isPlayerObject(int id){
GameObject obj = OBJECT.get(id)
if(obj instanceof PlayerObject && obj != null){
return true;
}
return false;
}
public boolean isNPCObject(int id){
GameObject obj = OBJECT.get(id)
if(obj instanceof NpcObject && obj != null){
return true;
}
return false;
}
//...
final PlayerObject pObject = isPlayerObject(objectID) ? OBJECT.get(id) : null;
I have an issue with realm. I receive a custom object from an API. I assign this object to a POJO object using retrofit. Within this object I have an ArrayList of the ToDoItemobject which extends RealmObject.
I receive the data correctly with all attributes, everything gets correctly assigned. I run it through my synchronization algorithm and save it to realm in a writing transaction. But when retrieving the data after realm.commit(); the attributes of the objects are all 0 or null.
The method isManaged()is always false, even after the writing transaction, which I don't understand because in the official documentation is states that a POJO can be converted to a managed object using the copyToRealm method.
I already tried a number of things: creating the GetItemResponseClass as RealmObject, but not possible since it has to extend JSONObject to correctly receive the data from the API. I also tried to write the whole list directly to realm but the result was the same.
As a side note, it can be that my method syncPendingLists has some logic errors, but I couldn't debug it yet, since the attributes were always o and null. Thanks for any help.
Here my code from the Activity:
public class MainActivity extends AppCompatActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Realm.init(this);
RealmConfiguration config = new RealmConfiguration.Builder().name("myrealm.realm").build();
Realm.setDefaultConfiguration(config);
realm = Realm.getDefaultInstance();
RealmResults<Counter> counterList = realm.where(Counter.class).findAll();
//setting up counterObject
if (counterList.isEmpty()) {
counterObject = new Counter();
COUNTER = counterObject.getCounter();
} else {
counterObject = counterList.get(0);
COUNTER = counterObject.getCounter();
}
initializeLists();
//Adding the Fragment
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment_container, new DoneListFragment(), "DoneListFragment");
ft.add(R.id.fragment_container, new PendingListFragment(), "PendingListFragment");
ft.commit();
RetrofitClient retrofitClient = new RetrofitClient();
Retrofit retrofit = retrofitClient.getClient();
mAPIInterface = retrofit.create(ToDoistAPIInterface.class);
}
public void getRemoteItems() {
final ArrayList<ToDoItem> onlineItems = new ArrayList<ToDoItem>();
JSONArray array = new JSONArray();
array.put("items");
String auxMessage = array.toString();
mAPIInterface.getItems(RetrofitClient.TOKEN, "*", auxMessage).enqueue(new Callback<GetItemsResponseClass>() {
#Override
public void onResponse(Call<GetItemsResponseClass> call, Response<GetItemsResponseClass> response) {
GetItemsResponseClass itemsResponseClass = new GetItemsResponseClass();
itemsResponseClass = response.body();
remoteItemsList = itemsResponseClass.getItems();
boolean test = remoteItemsList.get(0).isManaged(); //returns false
boolean test1 = remoteItemsList.get(0).isValid(); //returns true refers to singleton RealmObject
syncPendingLists(pendingItemList, remoteItemsList);
}
#Override
public void onFailure(Call<GetItemsResponseClass> call, Throwable t) {
Snackbar.make(floatingButton, "Ups - Couldn't sync items, next time, I promise", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
private void initializeLists() {
RealmResults<ToDoItem> realmToDoItemPendingList = realm.where(ToDoItem.class).equalTo("checkedOffline", false).findAll();
initializingArrayListFromDB(realmToDoItemPendingList, pendingItemList);
RealmResults<ToDoItem> realmToDoItemDoneList = realm.where(ToDoItem.class).equalTo("checkedOffline", true).findAll();
initializingArrayListFromDB(realmToDoItemDoneList, doneItemList);
}
private void initializingArrayListFromDB(RealmResults<ToDoItem> realmToDoItemPendingList, ArrayList<ToDoItem> arrayList) {
int h;
for (h = 0; h < realmToDoItemPendingList.size(); h++) {
arrayList.add(realmToDoItemPendingList.get(h));
}
}
public void syncPendingLists(ArrayList<ToDoItem> offlinePendingList, ArrayList<ToDoItem> onlinePendingList) {
//is my sync algorithm, the important part is the for loop at the end of this method
boolean hasMatch = false;
boolean itemChanged = false;
Date offlineDate = null;
Date onlineDate = null;
if (!offlinePendingList.isEmpty()) {
for (ToDoItem item1 : offlinePendingList) {
if (item1.getId() < 10000) {
try {
createNewRemoteItem(item1);
} catch (JSONException e) {
e.printStackTrace();
}
} else {
for (int i = 0; i < onlinePendingList.size(); i++) {
if (item1.getId() == onlinePendingList.get(i).getId()) {
hasMatch = true;
onlinePendingList.remove(onlinePendingList.get(i));
//Compare Fields
if (!item1.getContent().equals(onlinePendingList.get(i).getContent())) {
itemChanged = true;
}
if (item1.getPriority() != onlinePendingList.get(i).getPriority()) {
itemChanged = true;
}
if (!item1.getDate_string().equals(onlinePendingList.get(i).getDate_string())) {
itemChanged = true;
}
if (itemChanged == true) {
//Format edit dates to date
DateFormat format = new SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH);
try {
offlineDate = format.parse(item1.getDateAdded());
} catch (ParseException e) {
e.printStackTrace();
}
try {
onlineDate = format.parse(onlinePendingList.get(i).getDateAdded());
} catch (ParseException e) {
e.printStackTrace();
}
//compare dates to see which was last edited
if (offlineDate.compareTo(onlineDate) > 0) {
try {
deleteRemoteItem(onlinePendingList.get(i), "item_delete");
createNewRemoteItem(item1);
} catch (JSONException e) {
e.printStackTrace();
}
} else if (offlineDate.compareTo(onlineDate) < 0) {
addOrUpdateToDB(item1);
}
}
}
if (!hasMatch) {
deleteObjectFromDB(item1);
}
}
}
}
}
for (ToDoItem onlineItem1 : onlinePendingList) {
boolean isManaged1 = onlineItem1.isManaged(); //returns false, which is ok since it is not yet in the realm db
onlineItem1.setLocalId(counterObject.getCounter());
addOrUpdateToDB(onlineItem1);
boolean asdf = onlineItem1.isManaged(); //it returns false, but it should return true
incrementCounter(counterObject);
}
initializeLists();
getPendingListFragment().refreshFragment();
}
private void addOrUpdateToDB(ToDoItem newItem) {
boolean test2= newItem.isManaged(); //returns false
realm.beginTransaction();
realm.copyToRealmOrUpdate(newItem);
//realm.copyToRealm(newItem); //I tried this method as well, but no difference
realm.commitTransaction();
boolean test3= newItem.isManaged(); //returns false, and here is the problem, it should return true, shouldn't it?
assignValuesToToDoItem(itemWithValues, newItem);
saveCounterToDB(counterObject);
}
}
Here my class code of ToDoItem:
public class ToDoItem extends RealmObject implements Parcelable {
public static final Creator<ToDoItem> CREATOR = new Creator<ToDoItem>() {
#Override
public ToDoItem createFromParcel(Parcel in) {
return new ToDoItem(in);
}
#Override
public ToDoItem[] newArray(int size) {
return new ToDoItem[size];
}
};
#PrimaryKey
private long localId;
private String content;
private boolean checkedOffline = false;
private int priority;
private String date_string;
private String temp_id;
private long id;
private String date_added;
public ToDoItem(String name) {
this.content = name;
}
public ToDoItem() {
}
protected ToDoItem(Parcel in) {
localId = in.readLong();
content = in.readString();
checkedOffline = in.readByte() != 0;
priority = in.readInt();
date_string = in.readString();
temp_id = in.readString();
id = in.readLong();
date_added=in.readString();
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public boolean isCheckedOffline() {
return checkedOffline;
}
public void setCheckedOffline(boolean checkedOffline) {
this.checkedOffline = checkedOffline;
}
public Long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setRemote_id(Long remote_id) {
this.id = remote_id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public boolean isDone() {
return checkedOffline;
}
public String getDate_string() {
return date_string;
}
public void setDate_string(String date_string) {
this.date_string = date_string;
}
public long getLocalId() {
return this.localId;
}
public void setLocalId(long i) {
this.localId = i;
}
public String getTemp_id() {
return temp_id;
}
public void setTemp_id(String temp_id) {
this.temp_id = temp_id;
}
public String getDateAdded() {
return date_added;
}
public void setDateAdded(String dateAdded) {
this.date_added = dateAdded;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(localId);
dest.writeString(content);
dest.writeByte((byte) (checkedOffline ? 1 : 0));
dest.writeInt((priority));
dest.writeString(date_string);
dest.writeString(temp_id);
dest.writeLong(id);
dest.writeString(date_added);
}
#Override
public String toString() {
return "localId: " + localId + "; content: " + content;
}
}
And here the code for the GetItemsResponseClass:
public class GetItemsResponseClass extends JSONObject {
private String sync_token;
#SerializedName("temp_id_mapping")
private HashMap<String, Long> temp_id_mapping;
private boolean full_sync;
#SerializedName("items")
private ArrayList<ToDoItem> items;
public GetItemsResponseClass(){
}
public String getSync_token() {
return sync_token;
}
public void setSync_token(String sync_token) {
this.sync_token = sync_token;
}
public HashMap<String, Long> getTemp_id_mapping() {
return temp_id_mapping;
}
public void setTemp_id_mapping(HashMap<String, Long> temp_id_mapping) {
this.temp_id_mapping = temp_id_mapping;
}
public boolean isFull_sync() {
return full_sync;
}
public void setFull_sync(boolean full_sync) {
this.full_sync = full_sync;
}
public ArrayList<ToDoItem> getItems() {
return items;
}
public void setItems(ArrayList<ToDoItem> items) {
this.items = items;
}
}
EDIT: Apparently it is a desired behavior that the object does not get saved with its attributes. Consequently to assign the values you have to use getters and setters. I added the following method, however even when debugging with a watch, as stated in the official documentation the values do not get assigned:
private void assignValuesToToDoItem(ToDoItem itemWithValues, ToDoItem newItem) {
realm.beginTransaction();
newItem.setContent(itemWithValues.getContent()); //the content variable stays null
newItem.setCheckedOffline(itemWithValues.isDone()); //stays false
newItem.setPriority(itemWithValues.getPriority());
newItem.setDate_string(itemWithValues.getDate_string());
newItem.setTemp_id(itemWithValues.getTemp_id());
newItem.setId(itemWithValues.getId());
newItem.setDate_added(itemWithValues.getDate_added());
realm.commitTransaction();
}
I added this line assignValuesToToDoItem(itemWithValues, newItem); in the main activity in the method private void addOrUpdateToDB(ToDoItem newItem) {...}
Same result...
I found out 2 very important things:
The attributes are saved, however in the debugging window they appear to be 0, false or null
Even putting a Debugging Watch does not show the correct values.
To see the real value how it is in the database you have to add a Watch and put the watch directly on the getters of the object. In my case I added a Watch and typed in "newItem.getContent()". With this line I got the title of my object. However just putting a Watch with "newItem" shows "null".
copyToRealm() and copyToRealmOrUpdate() returns the managed proxy as a return value of the function you're calling.
realm.copyToRealmOrUpdate(newItem);
realm.commitTransaction();
boolean test3= newItem.isManaged(); //returns false, and it should return false
Should be
newItem = realm.copyToRealmOrUpdate(newItem);
realm.commitTransaction();
boolean test3= newItem.isManaged(); //returns true
I am new to Eclipse RCP/Plug-ins and SWT. I want to reorder table items via drag-and-drop.
I have a TableViewer which contains a table with my custom elements of type ITask (еach of my custom elements is wrapped in TableItem). All tutorials I found are about trees or dragging between different tables which is not what I need.
So I want to know how to reorder the table rows via drag-and-drop.
It's a bit long, but you can make this work in your code with a few changes. I did not included the imports; Eclipse can do it for you automatically.
I used Spring's BeanUtils class but you can use any lib (or write your own) that can deepcopy POJOs. I assume that your ITask has a setOrder(int) method and is Serializable (and it qualifies for a POJO)
You need to create a Transfer-type for your ITask: SimpleObjectTransfer is IBM's code, from Eclipse GEF. You can Google/GrepCode it.
public final class TaskTransfer extends SimpleObjectTransfer {
public static final TaskTransfer INSTANCE = new TaskTransfer();
private TaskTransfer() {
}
#Override
protected String getTypeNamePrefix() {
return "TASK_TRANSFER_FORMAT";
}
}
A ViewerDropAdapter:
public class MyDropAdapter<TM extends ITask> extends ViewerDropAdapter {
private final Class<TM> targetModelClass;
private List<TM> listOfModels;
protected MyDropAdapter(Viewer viewer, Class<TM> targetModelClass, List<TM> listOfModels) {
super(viewer);
this.listOfModels = listOfModels;
this.targetModelClass = targetModelClass;
}
#Override
public boolean performDrop(Object arg0) {
boolean ret = false;
TM targetModel = targetModelClass.cast(determineTarget(getCurrentEvent()));
if (targetModel != null) {
if (List.class.isAssignableFrom(arg0.getClass())) {
ret = processDropToTable(targetModel, arg0);
getViewer().refresh();
}
}
return ret;
}
public final boolean processDropToTable(TM targetModel, Object data) {
List<TM> transferredModels = (List<TM>) data;
List<TM> copyOfTransferredModels = transferredModels;
switch (getCurrentOperation()) {
case DND.DROP_COPY:
copyOfTransferredModels = deepCopyBeanList(transferredModels, new String[]{});
break;
case DND.DROP_MOVE:
// moving
break;
default:
throw new UnsupportedOperationException(getCurrentOperation() + " is not supported!");
}
adjustPosition(transferredModels, copyOfTransferredModels, targetModel);
return true;
}
private void adjustPosition(List<TM> transferredModels, List<TM> copyOfTransferredModels, TM targetModel) {
int transferredObjectPosition = listOfModels.indexOf(transferredModels.get(0));
listOfModels.removeAll(copyOfTransferredModels);
addModelsToNewLocation(copyOfTransferredModels, targetModel, listOfModels, transferredObjectPosition);
for (int i = 0; i < listOfModels.size(); i++) {
int orderPosition = i * 10 + 10;
listOfModels.get(i).setOrder(orderPosition);
}
}
protected void addModelsToNewLocation(List<TM> transferredModels, TM targetModel, List<TM> targetList, int transferredObjectPosition) {
switch (determineLocation(getCurrentEvent())) {
case LOCATION_AFTER:
case LOCATION_ON:
int i;
if (!transferredModels.contains(targetModel)) {
i = targetList.indexOf(targetModel) + 1;
} else {
i = transferredObjectPosition;
}
targetList.addAll(i, transferredModels);
break;
case LOCATION_BEFORE:
if (!transferredModels.contains(targetModel)) {
i = targetList.indexOf(targetModel);
} else {
i = transferredObjectPosition;
}
targetList.addAll(i, transferredModels);
break;
case LOCATION_NONE:
default:
break;
}
}
private List<TM> deepCopyBeanList(List<TM> transferredModels, String[] ignoreProperties) {
List<TM> targetList = new LinkedList<TM>();
for (TM element : transferredModels) {
try {
#SuppressWarnings("unchecked")
TM copy = (TM) element.getClass().newInstance();
BeanUtils.copyProperties(element, copy, ignoreProperties);
targetList.add(copy);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return targetList;
}
#Override
public boolean validateDrop(Object arg0, int arg1, TransferData arg2) {
boolean ret = false;
for (Transfer t : new Transfer[]{TaskTransfer.INSTANCE}) {
if (t.isSupportedType(arg2)) {
ret = true;
break;
}
}
return ret;
}
}
A DragSourceListener
public class MyDragSourceListener implements DragSourceListener {
private final Viewer dragSourceViewer;
private final boolean multiObjectsEnabled;
private final Class<?> transferrableElementClass;
private Object[] draggedObjects;
public MyDragSourceListener(Viewer dragSourceViewer, boolean multiObjectsEnabled, Class<?> transferrableElementClass) {
this.dragSourceViewer = dragSourceViewer;
this.multiObjectsEnabled = multiObjectsEnabled;
this.transferrableElementClass = transferrableElementClass;
}
#Override
public void dragStart(DragSourceEvent event) {
Control source = ((DragSource) event.getSource()).getControl();
draggedObjects = null;
if (dragSourceViewer.getControl().equals(source)) {
if (multiObjectsEnabled) {
draggedObjects = ((StructuredSelection) dragSourceViewer.getSelection()).toArray();
} else {
draggedObjects = new Object[]{((StructuredSelection) dragSourceViewer.getSelection()).getFirstElement()};
}
}
event.doit = draggedObjects.length > 0 && transferredDataIsSupported();
}
private boolean transferredDataIsSupported() {
boolean ret = true;
for (Object o : draggedObjects) {
if (o == null || !transferrableElementClass.isAssignableFrom(o.getClass())) {
ret = false;
break;
}
}
return ret;
}
#Override
public void dragSetData(DragSourceEvent event) {
event.data = Arrays.asList(draggedObjects);
}
#Override
public void dragFinished(DragSourceEvent event) {
if (event.detail != DND.DROP_NONE) {
dragSourceViewer.refresh();
}
draggedObjects = null;
}
}
And place a code somewhat like this in your View:
List<ITask> tasks = new WritableList(new ArrayList<ITask>(), ITask.class);
// Let's say tableViewerTasks is your TableViewer's name
DragSource sourceTasks = new DragSource(tblTasks, DND.DROP_MOVE);
sourceTasks.setTransfer(new Transfer[]{TaskTransfer.INSTANCE});
sourceTasks.addDragListener(new MyDragSourceListener(tableViewerTasks, true, ITask.class));
DropTarget targetTasks = new DropTarget(tblTasks, DND.DROP_MOVE);
targetTasks.setTransfer(new Transfer[]{TaskTransfer.INSTANCE});
targetTasks.addDropListener(new MyDropAdapter<ITask>(tableViewerTasks, ITask.class, tasks));
I want to support hibernate.hbm2ddl.auto = auto in my project. I use Hibernate + SQLite with SQLiteDialect:
package app.sqlite;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.JDBCException;
import org.hibernate.ScrollMode;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.function.AbstractAnsiTrimEmulationFunction;
import org.hibernate.dialect.function.NoArgSQLFunction;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.dialect.pagination.AbstractLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.LimitHelper;
import org.hibernate.dialect.unique.DefaultUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.exception.DataException;
import org.hibernate.exception.JDBCConnectionException;
import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter;
import org.hibernate.exception.spi.ViolatedConstraintNameExtracter;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.mapping.Column;
import org.hibernate.type.StandardBasicTypes;
public class SQLiteDialect extends Dialect {
private final UniqueDelegate uniqueDelegate;
public SQLiteDialect() {
registerColumnType(Types.BIT, "boolean");
//registerColumnType(Types.TINYINT, "tinyint");
//registerColumnType(Types.SMALLINT, "smallint");
//registerColumnType(Types.INTEGER, "integer");
//registerColumnType(Types.BIGINT, "bigint");
//registerColumnType(Types.FLOAT, "float");
//registerColumnType(Types.REAL, "real");
//registerColumnType(Types.DOUBLE, "double");
//registerColumnType(Types.NUMERIC, "numeric($p, $s)");
registerColumnType(Types.DECIMAL, "decimal");
registerColumnType(Types.CHAR, "char");
registerColumnType(Types.VARCHAR, "varchar($l)");
registerColumnType(Types.LONGVARCHAR, "longvarchar");
//registerColumnType(Types.DATE, "date");
//registerColumnType(Types.TIME, "time");
registerColumnType(Types.TIMESTAMP, "datetime");
registerColumnType(Types.BINARY, "blob");
registerColumnType(Types.VARBINARY, "blob");
registerColumnType(Types.LONGVARBINARY, "blob");
//registerColumnType(Types.BLOB, "blob");
//registerColumnType(Types.CLOB, "clob");
//registerColumnType(Types.BOOLEAN, "boolean");
registerFunction( "concat", new VarArgsSQLFunction(StandardBasicTypes.STRING, "", "||", "") );
registerFunction( "mod", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "?1 % ?2" ) );
registerFunction( "quote", new StandardSQLFunction("quote", StandardBasicTypes.STRING) );
registerFunction( "random", new NoArgSQLFunction("random", StandardBasicTypes.INTEGER) );
registerFunction( "round", new StandardSQLFunction("round") );
registerFunction( "substr", new StandardSQLFunction("substr", StandardBasicTypes.STRING) );
registerFunction( "trim", new AbstractAnsiTrimEmulationFunction() {
protected SQLFunction resolveBothSpaceTrimFunction() {
return new SQLFunctionTemplate(StandardBasicTypes.STRING, "trim(?1)");
}
protected SQLFunction resolveBothSpaceTrimFromFunction() {
return new SQLFunctionTemplate(StandardBasicTypes.STRING, "trim(?2)");
}
protected SQLFunction resolveLeadingSpaceTrimFunction() {
return new SQLFunctionTemplate(StandardBasicTypes.STRING, "ltrim(?1)");
}
protected SQLFunction resolveTrailingSpaceTrimFunction() {
return new SQLFunctionTemplate(StandardBasicTypes.STRING, "rtrim(?1)");
}
protected SQLFunction resolveBothTrimFunction() {
return new SQLFunctionTemplate(StandardBasicTypes.STRING, "trim(?1, ?2)");
}
protected SQLFunction resolveLeadingTrimFunction() {
return new SQLFunctionTemplate(StandardBasicTypes.STRING, "ltrim(?1, ?2)");
}
protected SQLFunction resolveTrailingTrimFunction() {
return new SQLFunctionTemplate(StandardBasicTypes.STRING, "rtrim(?1, ?2)");
}
} );
uniqueDelegate = new SQLiteUniqueDelegate(this);
}
// database type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Override
public String getCastTypeName(int code) {
return super.getCastTypeName(code); // FIXME
}
// IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Override
public boolean supportsIdentityColumns() {
return true;
}
/*
public boolean supportsInsertSelectIdentity() {
return true; // As specified in NHibernate dialect
}
*/
#Override
public boolean hasDataTypeInIdentityColumn() {
return false; // As specified in NHibernate dialect
}
/*
public String appendIdentitySelectToInsert(String insertString) {
return new StringBuffer(insertString.length()+30). // As specified in NHibernate dialect
append(insertString).
append("; ").append(getIdentitySelectString()).
toString();
}
*/
#Override
public String getIdentityColumnString(int type) {
// return "integer primary key autoincrement";
return "integer";
}
#Override
public String getIdentitySelectString(String table, String column, int type) {
return "select last_insert_rowid()";
}
// limit/offset support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private static final AbstractLimitHandler LIMIT_HANDLER = new AbstractLimitHandler() {
#Override
public String processSql(String sql, RowSelection selection) {
final boolean hasOffset = LimitHelper.hasFirstRow( selection );
return sql + (hasOffset ? " limit ? offset ?" : " limit ?");
}
#Override
public boolean supportsLimit() {
return true;
}
#Override
public boolean bindLimitParametersInReverseOrder() {
return true;
}
};
#Override
public LimitHandler getLimitHandler() {
return LIMIT_HANDLER;
}
// lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Override
public boolean supportsLockTimeouts() {
return false; // may be http://sqlite.org/c3ref/db_mutex.html ?
}
#Override
public String getForUpdateString() {
return "";
}
#Override
public boolean supportsOuterJoinForUpdate() {
return false;
}
/*
#Override
public boolean dropTemporaryTableAfterUse() {
return true; // temporary tables are only dropped when the connection is closed. If the connection is pooled...
}
*/
// current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Override
public boolean supportsCurrentTimestampSelection() {
return true;
}
public boolean isCurrentTimestampSelectStringCallable() {
return false;
}
#Override
public String getCurrentTimestampSelectString() {
return "select current_timestamp";
}
// SQLException support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private static final int SQLITE_BUSY = 5;
private static final int SQLITE_LOCKED = 6;
private static final int SQLITE_IOERR = 10;
private static final int SQLITE_CORRUPT = 11;
private static final int SQLITE_NOTFOUND = 12;
private static final int SQLITE_FULL = 13;
private static final int SQLITE_CANTOPEN = 14;
private static final int SQLITE_PROTOCOL = 15;
private static final int SQLITE_TOOBIG = 18;
private static final int SQLITE_CONSTRAINT = 19;
private static final int SQLITE_MISMATCH = 20;
private static final int SQLITE_NOTADB = 26;
#Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return new SQLExceptionConversionDelegate() {
#Override
public JDBCException convert(SQLException sqlException, String message, String sql) {
final int errorCode = JdbcExceptionHelper.extractErrorCode(sqlException);
if (errorCode == SQLITE_TOOBIG || errorCode == SQLITE_MISMATCH) {
return new DataException(message, sqlException, sql);
} else if (errorCode == SQLITE_BUSY || errorCode == SQLITE_LOCKED) {
return new LockAcquisitionException(message, sqlException, sql);
} else if ((errorCode >= SQLITE_IOERR && errorCode <= SQLITE_PROTOCOL) || errorCode == SQLITE_NOTADB) {
return new JDBCConnectionException(message, sqlException, sql);
}
// returning null allows other delegates to operate
return null;
}
};
}
public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() {
return EXTRACTER;
}
private static final ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
#Override
protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException {
final int errorCode = JdbcExceptionHelper.extractErrorCode(sqle);
if (errorCode == SQLITE_CONSTRAINT) {
return extractUsingTemplate("constraint ", " failed", sqle.getMessage());
}
return null;
}
};
// union subclass support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Override
public boolean supportsUnionAll() {
return true;
}
// DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Override
public boolean canCreateSchema() {
return false;
}
#Override
public boolean hasAlterTable() {
return false; // As specified in NHibernate dialect
}
#Override
public boolean dropConstraints() {
return false;
}
#Override
public boolean qualifyIndexName() {
return false;
}
/*
public String getAddColumnString() {
return "add column";
}
*/
#Override
public String getDropForeignKeyString() {
throw new UnsupportedOperationException("No drop foreign key syntax supported by SQLiteDialect");
}
#Override
public String getAddForeignKeyConstraintString(String constraintName,
String[] foreignKey, String referencedTable, String[] primaryKey,
boolean referencesPrimaryKey) {
throw new UnsupportedOperationException("No add foreign key syntax supported by SQLiteDialect");
}
#Override
public String getAddPrimaryKeyConstraintString(String constraintName) {
throw new UnsupportedOperationException("No add primary key syntax supported by SQLiteDialect");
}
#Override
public boolean supportsCommentOn() {
return true;
}
#Override
public boolean supportsIfExistsBeforeTableName() {
return true;
}
/* not case insensitive for unicode characters by default (ICU extension needed)
public boolean supportsCaseInsensitiveLike() {
return true;
}
*/
#Override
public boolean doesReadCommittedCauseWritersToBlockReaders() {
return true; // TODO Validate (WAL mode...)
}
public boolean doesRepeatableReadCauseReadersToBlockWriters() {
return true;
}
#Override
public boolean supportsTupleDistinctCounts() {
return false;
}
public int getInExpressionCountLimit() {
return 1000; // Compile/runtime time option: http://sqlite.org/limits.html#max_variable_number
}
#Override
public UniqueDelegate getUniqueDelegate() {
return uniqueDelegate;
}
private static class SQLiteUniqueDelegate extends DefaultUniqueDelegate {
public SQLiteUniqueDelegate(Dialect dialect) {
super(dialect);
}
#Override
public String getColumnDefinitionUniquenessFragment(Column column) {
return " unique";
}
}
#Override
public String getSelectGUIDString() {
return "select hex(randomblob(16))";
}
#Override
public ScrollMode defaultScrollMode() {
return ScrollMode.FORWARD_ONLY;
}
}
and the Error:
Caused by: java.lang.UnsupportedOperationException: No add column syntax supported by app.sqlite.SQLiteDialect
at org.hibernate.dialect.Dialect.getAddColumnString(Dialect.java:2148)
at org.hibernate.mapping.Table.sqlAlterStrings(Table.java:449)
at org.hibernate.tool.schema.internal.SchemaMigratorImpl.migrateTable(SchemaMigratorImpl.java:254)
at org.hibernate.tool.schema.internal.SchemaMigratorImpl.doMigrationToTargets(SchemaMigratorImpl.java:170)
at org.hibernate.tool.schema.internal.SchemaMigratorImpl.doMigration(SchemaMigratorImpl.java:60)
at org.hibernate.tool.hbm2ddl.SchemaUpdate.execute(SchemaUpdate.java:133)
at org.hibernate.tool.hbm2ddl.SchemaUpdate.execute(SchemaUpdate.java:101)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:470)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:444)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:708)
at app.sqlite.MailDBConnector.connect(MailDBConnector.java:54)
When I uncomment:
public String getAddColumnString() {
return "add column";
}
The Error is:
org.hibernate.tool.schema.spi.SchemaManagementException: Unable to execute schema management to JDBC target [alter table mailMessage add column flag integer]
EDIT:
I still workaround the issue, I need to implement method getAddColumnString inside org.hibernate.Dialect to make it work with SQLite database.
Default implementation for MySQL dialect is:
public String getAddColumnString() {return "add column";}
Sadly guys from Hibernate forum told me that SQLite is not supported by hibernate, there are some undefined plans to support it in the future so i dont think there is easy way without deep digging inside hibernate code. As alternative for SQLite that work with hibernate.hbm2ddl.auto=update it can be embedded PostgreSQL