Hibernate + SQLite updating table with hibernate.hbm2ddl.auto = auto - java

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

Related

error: Not sure how to handle insert method's return type

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 :-

Android java, Dao interface in the database class seems to be weird

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

Realm does not save attributes, object is not managed although copied to Realm

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

Make Custom Control Generic

I have created a Custom JavaFX Control, Call it ComboBoxTablePopup, it's a Generic Control witch takes a list of items of type S from the user.
Internally, i have used some sort of filtering in the TableView(a child of ComboboxTablePopup Contrl).So i have used :
FiltredList.setPridcate(S -> {
if (pr == null || pr.isEmpty() || pr.length() == 0)
return true;
if (((Engine) S).getDesignation().toLowerCase().contains(pr.toLowerCase())) {
return true;
} else
return false;).
Because i'm using generic class and the list can be of any type i have to cast filtredlist items to well known object in ordre to get filtring work.
So, how can i make the predicate function generic, so i can work with any object and filtre it ?
Here is the code of my CustomControl:
public class ComboBoxTablePopup<S> extends ComboBoxBase {
private
ObservableList<TableColumn> columns = FXCollections.emptyObservableList();
public ObservableList<TableColumn> getColumns() {
return columns;
}
public void setColumns(ObservableList<TableColumn> columns) {
this.columns = columns;
}
/***************************************************************************
* *
* Static properties and methods *
* *
**************************************************************************/
private static <T> StringConverter<T> defaultStringConverter() {
return new StringConverter<T>() {
#Override
public String toString(T t) {
return t == null ? null : t.toString();
}
#Override
public T fromString(String string) {
return (T) string;
}
};
}
/***************************************************************************
* *
* Constructors *
* *
**************************************************************************/
/**
* Creates a default ComboboxTablePopup instance with an empty
* {#link #itemsProperty() items} list and default
* {#link #selectionModelProperty() selection model}.
*/
public ComboBoxTablePopup() {
this(FXCollections.<S>emptyObservableList());
}
/**
* Creates a default ComboboxTablePopup instance with the provided items list and
* a default {#link #selectionModelProperty() selection model}.
*/
public ComboBoxTablePopup(ObservableList<S> items) {
setItems(items);
getStyleClass().add(DEFAULT_STYLE_CLASS);
setEditable(true);
setPromptText("Plz Search for a pirticular term");
}
public ComboBoxTablePopup(ObservableList<S> items, ObservableList<TableColumn> columns) {
this(items);
this.columns = columns;
}
private static final String DEFAULT_STYLE_CLASS = "combobox-table-popup";
private ReadOnlyObjectWrapper<TextField> editor;
private ObjectProperty<ObservableList<S>> items = new SimpleObjectProperty<ObservableList<S>>(this, "items");
public final void setItems(ObservableList<S> value) {
itemsProperty().set(value);
}
public final ObservableList<S> getItems() {
return items.get();
}
public ObjectProperty<ObservableList<S>> itemsProperty() {
return items;
}
// Converter
public ObjectProperty<StringConverter<S>> converterProperty() {
return converter;
}
private ObjectProperty<StringConverter<S>> converter =
new SimpleObjectProperty<StringConverter<S>>(this, "converter", ComboBoxTablePopup.<S>defaultStringConverter());
public final void setConverter(StringConverter<S> value) {
converterProperty().set(value);
}
public final StringConverter<S> getConverter() {
return converterProperty().get();
}// Create a symmetric (format/parse) converter with the default locale.
// Editor
public TextField getEditor() {
return editorProperty().get();
}
public ReadOnlyObjectProperty<TextField> editorProperty() {
if (editor == null) {
editor = new ReadOnlyObjectWrapper<TextField>(this, "editor");
editor.set(new ComboBoxListViewSkin.FakeFocusTextField());
}
return editor.getReadOnlyProperty();
}
#Override
protected Skin<?> createDefaultSkin() {
return new ComboBoxTablePopupSkin<>(this);
}
}
public class ComboBoxTablePopupSkin<S> extends ComboBoxPopupControl {
private ComboBoxTablePopup comboBoxTablePopup;
private ObservableList<S> comboboxTablePopupItems;
private TextField displayNode;
private TableView<S> tableViewPopupContent;
FilteredList<S> filtredList;
private SortedList<S> sortedList;
private Predicate<S> predicate;
private final InvalidationListener itemsObserver;
private final ListChangeListener<S> tableViewItemsListener = new ListChangeListener<S>() {
#Override
public void onChanged(ListChangeListener.Change<? extends S> c) {
getSkinnable().requestLayout();
}
};
private final WeakListChangeListener<S> weakListViewItemsListener =
new WeakListChangeListener<S>(tableViewItemsListener);
public ComboBoxTablePopupSkin(ComboBoxTablePopup comboBoxTablePopup) {
super(comboBoxTablePopup, new ComboBoxTablePopupBehavior(comboBoxTablePopup));
this.comboBoxTablePopup = comboBoxTablePopup;
setupTablePredicate();
updateComboBoxTablePopupItems();
itemsObserver = observable -> {
updateComboBoxTablePopupItems();
updateTableViewItems();
};
this.comboBoxTablePopup.itemsProperty().addListener(new WeakInvalidationListener(itemsObserver));
tableViewPopupContent = createTableView();
tableViewPopupContent.setManaged(false);
getChildren().add(tableViewPopupContent);
updateTableViewItems();
getEditor().textProperty().addListener((obv, oldValue, newValue) -> {
if (!newValue.isEmpty())
comboBoxTablePopup.show();
filtreData(newValue);
});
registerChangeListener(comboBoxTablePopup.itemsProperty(), "ITEMS");
registerChangeListener(comboBoxTablePopup.valueProperty(), "VALUE");
registerChangeListener(comboBoxTablePopup.editorProperty(), "EDITABLE");
}
private void setupTablePredicate() {
}
private void updateTableViewItems() {
filtredList = new FilteredList<S>(comboboxTablePopupItems, p -> true);
sortedList = new SortedList<S>(filtredList);
sortedList.comparatorProperty().bind(tableViewPopupContent.comparatorProperty());
tableViewPopupContent.setItems(sortedList);
}
private void filtreData(String pr) {
String data = pr;
filtredList.setPredicate(engine -> {
if (pr == null || pr.isEmpty() || pr.length() == 0)
return true;
if (((Engine) engine).getDesignation().toLowerCase().contains(pr.toLowerCase())) {
return true;
} else
return false;
});
}
public void updateComboBoxTablePopupItems() {
comboboxTablePopupItems = comboBoxTablePopup.getItems();
comboboxTablePopupItems = comboboxTablePopupItems == null ? FXCollections.<S>emptyObservableList() : comboboxTablePopupItems;
}
private TableView<S> createTableView() {
final TableView<S> tableView = new TableView<>();
tableView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
tableView.setFocusTraversable(false);
tableView.getSelectionModel().selectedItemProperty().addListener(o -> {
int index = tableView.getSelectionModel().getSelectedIndex();
System.out.println("selected item changed");
});
for (TableColumn tblColumn : tableColumns()) {
tableView.getColumns().add(tblColumn);
}
tableView.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ENTER ||
e.getCode() == KeyCode.ESCAPE ||
e.getCode() == KeyCode.SPACE) {
S selectedItem = tableView.getSelectionModel().getSelectedItem();
if (selectedItem != null) {
System.out.println(((Engine) selectedItem).getDesignation());
getEditor().setText(((Engine) selectedItem).getDesignation());
comboBoxTablePopup.setValue(selectedItem);
comboBoxTablePopup.hide();
}
}
});
return tableView;
}
private ObservableList<TableColumn> tableColumns() {
return ((ComboBoxTablePopup) getSkinnable()).getColumns();
}
#Override
protected Node getPopupContent() {
return this.tableViewPopupContent;
}
#Override
protected TextField getEditor() {
return ((ComboBoxTablePopup) getSkinnable()).getEditor();
}
#Override
protected StringConverter getConverter() {
return ((ComboBoxTablePopup) getSkinnable()).getConverter();
}
#Override
public Node getDisplayNode() {
if (displayNode == null) {
displayNode = getEditableInputNode();
displayNode.getStylesheets().add("ComboBoxTable-display-node");
updateDisplayNode();
}
displayNode.setEditable(comboBoxTablePopup.isEditable());
return displayNode;
}
#Override
protected void handleControlPropertyChanged(String p) {
super.handleControlPropertyChanged(p);
if ("TEXT".equals(p)) {
if (!getEditor().textProperty().get().isEmpty()) {
comboBoxTablePopup.show();
}
filtreData(getEditor().textProperty().get());
} else if ("ITEMS".equals(p)) {
updateComboBoxTablePopupItems();
} else if ("EDITABLE".equals(p)) {
getEditableInputNode();
}
}
}
Use the StringConverter to convert the item to a String or use a similar property.
public class ComboBoxTablePopupSkin<S> extends ComboBoxPopupControl<S> {
...
private static final StringConverter DEFAULT_CONVERTER = new StringConverter() {
#Override
public String toString(Object o) {
return o == null ? null : o.toString();
}
#Override
public Object fromString(String s) {
throw new UnsupportedOperationException();
}
};
public final StringConverter<S> getConverter() {
StringConverter<S> converter = converterProperty().get();
// fix in case the property is set to a null
return converter == null ? DEFAULT_CONVERTER : converter;
}
private void filtreData(String pr) {
if (pr == null || pr.isEmpty()) {
filtredList.setPredicate(null);
} else {
final StringConverter<S> converter = ((ComboBoxTablePopup<S>) getSkinnable()).getConverter();
final String data = pr.toLowerCase();
filtredList.setPredicate(object -> {
String s = converter.toString(object);
return s != null && s.toLowerCase().contains(data);
});
}
}

Java implementing comparator on ArrayList

I'm quite new to Java so this is probably pretty straight forward question.
I want to sort an ArrayList in the class MediaLib based on the natural order of a specified key.
I can't work out how to use my comparator (compareTo(MediaInterface, key)) which is in the Media class. Whats the best way to go about this?
package assign1;
import java.util.*;
public class Media implements MediaInterface {
private Map<String, Object> fields;
private static int compare;
public Media(String title, String format) {
fields = new TreeMap<String, Object>();
fields.put("title", title);
fields.put("format", format);
}
public Object get(String key) {
return fields.get(key);
}
public void put(String key, Object value) {
fields.put(key, value);
}
public boolean hasKeywords(String[] words, boolean combineWithAND) {
Collection<Object> values = (Collection<Object>) fields.values();
int count = 0;
int size = 0;
for (String s: words) {
for (Object o: values) {
String t = o.toString();
if (t.indexOf(s) >= 0) {
count++;
break;
}
}
size++;
}
if ((count == 0 && !combineWithAND) || (combineWithAND && (count != size))) {
return false;
}
return true;
}
public int compareTo(MediaInterface mi, String key) { //<<<<<<<------calling this!!
if (mi == null)
throw new NullPointerException();
Media m = (Media) mi;
Comparable mValue = (Comparable) m.get(key);
Comparable lValue = (Comparable) fields.get(key);
if ((mValue == null) && (lValue == null)){
return 0;
}
if ((lValue == null)){
return 1;
}
if ((mValue == null)){
return -1;
}
return (lValue).compareTo(mValue);
}
#Override
public int compareTo(MediaInterface mi) {
if (mi == null)
throw new NullPointerException();
Media m = (Media) mi;
Set<String> lSet = fields.keySet();
if (compareTo(m, "title") != 0) {
return compareTo(m, "title");
}
if (compareTo(m, "year") != 0) {
return compareTo(m, "year");
}
for (String s: lSet) {
if (compareTo(m, s) != 0) {
return compareTo(m, s);
}
}
return 0;
}
public boolean equals(Object object) {
if (object == null)
return false;
if (!(object instanceof Media))
return false;
Media m = (Media) object;
if (compareTo(m) != 0) {
return false;
}
return true;
}
}
package assign1;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
public class MediaLib implements Searchable {
private ArrayList<MediaInterface> media;
public MediaLib() {
media = new ArrayList<MediaInterface>();
}
#Override
public void add(MediaInterface mi) {
if (media.isEmpty()) {
media.add(mi);
}
else {
for (MediaInterface m: media) {
if (mi.equals(m)) {
return;
}
}
media.add(mi);
}
}
#Override
public boolean contains(MediaInterface mi) {
for (MediaInterface m: media) {
if (mi.equals(m)) {
return true;
}
}
return false;
}
#Override
public Collection<MediaInterface> findByKeyword(String[] words, boolean combineWithAND) {
Collection<MediaInterface> foundList = new ArrayList<MediaInterface>();
for (MediaInterface mi: media) {
if (mi.hasKeywords(words, combineWithAND)) {
foundList.add(mi);
}
}
return foundList;
}
#Override
public Collection<MediaInterface> findByTitle(String str) {
Collection<MediaInterface> foundList = new ArrayList<MediaInterface>();
for (MediaInterface mi: media) {
if ((mi.get("title")).equals(str)) {
foundList.add(mi);
}
}
return foundList;
}
#Override
public Collection<MediaInterface> getAllWithFormat(String formatName) {
Collection<MediaInterface> foundList = new ArrayList<MediaInterface>();
for (MediaInterface mi: media) {
if ((mi.get("format")).equals(formatName)) {
foundList.add(mi);
}
}
return foundList;
}
public Collection<MediaInterface> getAll() {
Collection<MediaInterface> fullList = new ArrayList<MediaInterface>();
for (MediaInterface mi: media) {
fullList.add(mi);
}
return fullList;
}
#Override
public void removeAllWithKeyword(String[] words, boolean combineWithAND) {
Collection<MediaInterface> foundList = findByKeyword(words, combineWithAND);
for (MediaInterface mi: foundList) {
media.remove(mi);
}
}
#Override
public void removeAllWithFormat(String format) {
Collection<MediaInterface> foundList = getAllWithFormat(format);
for (MediaInterface mi: foundList) {
media.remove(mi);
}
}
#Override
public void sort() {
Collections.sort(media);
}
#Override
public void sort(final String fieldName) {
Collections.sort(media, new Media.compareTo(MediaInterface, fieldName)) //<<<<<--------Trying to call compareTo()
}
}
public void parse(java.io.BufferedReader br) throws java.io.IOException {
while(br.readLine()!= null) {
Media mi = new Media(/n br.readLine(), br.readLine());
while
}
}
}
You already implement the Comparable interface in your MediaInterface class, this is a generic interface, so you then implement Comparable<MediaInterface> which will then require you to implement a method with the signature
public int compareTo(final MediaInterface other)
This is why your call to Collections.sort(media); compiles
In order to sort by a specific field name, you need to provide an instance of a Comparator, the easiest way to do this will be to create an inner class in your Media class which you can then pass into Collections.sort. For example
public class Media implements MediaInterface {
public static final class FieldComparator implements Comparator<Media> {
private final String field;
public FieldComparator(final String field) {
this.field = field;
}
public int compare(final Media a, final Media b) {
// implementation to compare a.field to b.field
}
}
}
You can then rewrite your second sort method as
#Override
public void sort(final String fieldName) {
Collections.sort(media, new Media.FieldComparator(fieldName));
}

Categories

Resources