Static variables with inheritance [duplicate] - java

I have the following class which I'm using as the base of all the models in my project:
public abstract class BaseModel
{
static String table;
static String idField = "id";
public static boolean exists(long id) throws Exception
{
Db db = Util.getDb();
Query q = db.query();
q.select( idField ).whereLong(idField, id).limit(1).get(table);
return q.hasResults();
}
//snip..
}
I'm then trying to extend from it, in the following way:
public class User extends BaseModel
{
static String table = "user";
//snip
}
However, if I try to do the following:
if ( User.exists( 4 ) )
//do something
Then, rather than the query: "SELECT id FROM user WHERE id = ?", it is producing the query: "SELECT id from null WHERE id = ?". So, the overriding of the table field in the User class doesn't seem to be having any effect.
How do I overcome this? If I added a setTable() method to BaseModel, and called setTable() in the constructor of User, then will the new value of table be available to all methods of the User class as well?

You cannot override static methods or fields of any type in Java.
public class User extends BaseModel
{
static String table = "user";
//snip
}
This creates a new field User#table that just happens to have the same name as BaseModel#table. Most IDEs will warn you about that.
If you change the value of the field in BaseModel, it will apply to all other model classes as well.
One way is to have the base methods generic
protected static boolean exists(String table, long id) throws Exception
{
Db db = Util.getDb();
Query q = db.query();
q.select( idField ).whereLong(idField, id).limit(1).get(table);
return q.hasResults();
}
and use it in the subclass
public static boolean exists(long id)
{
return exists("user", id);
}
If you want to use the field approach, you have to create a BaseDAO class and have a UserDAO (one for each model class) that sets the field accordingly. Then you create singleton instances of all the daos.

Because Java doesn't allow you to override static members, you basically need to resort to the slightly more verbose but overall nicer singleton pattern, wherein you're still conceptually writing "static" code, but you're technically using (global/singleton/"static") instances, so you're not restricted by the limitations of static.
(note that you also need to use methods because fields don't participate in polymorphism, and thus cannot be overridden)
public abstract class BaseTable {
public abstract String table();
public String idField() { return "id"; }
public boolean exists(long id) {
// don't build queries this way in real life though!
System.out.println("SELECT count(*) FROM " + table() + " WHERE " + idField() + " = " + id);
return true;
}
}
public class UserTable extends BaseTable {
public static final User INSTANCE = new UserTable();
private UseTabler() {}
#Override public String table() { return "user"; }
}
public class PostTable extends BaseTable {
public static final Post INSTANCE = new PostTable();
private PostTable() {}
#Override public String table() { return "post"; }
}
public static void main(String[] args) {
UserTable.INSTANCE.exists(123);
PostTable.INSTANCE.exists(456);
}
Outputs:
SELECT count(*) FROM user WHERE id = 123
SELECT count(*) FROM post WHERE id = 456

In order to do what you are looking to do, don't make table static in the BaseModel. Then in the other classes that inherit from BaseModel, you can set table in the default constructor to whatever you wish.
static {
table = "user";
}

Related

If is it possible save the same object to two different table Room?

Let's say I have obj
class Human{
List<Car> demagedCars = new ArrayList();
List<Car> newCars = new ArrayList();
}
#Entity(tableName = "carstable")
class Car{
#PrimaryKey(autogenerate = true)
public long id;
public String name;
}
So, room will create one table with name carstable I will save to this table objects from two lists newCars and demagedCars
How I can get it back to two lists?
I thought that it is possible to create two tables demagedcarstable and newcarstable
And save according to the list demagedCars to demagedcarstable and newCars to newcarstable
But as far as I understood there is no way with room to create to tables?
I don't wan't to change my object...
All that I need is create in one database 2 tables. And when I get list of demagedCars all that I need is to tell him I want you to save to demagedcarstable and when I get newCars list I tell him I want you to save to newcarstable. So as a result I have 1 database and 2 tables and it is up to me where I want to save this Car object. Question is: is it possible?
Feel free to ask
From a structural point of view, I believe you should store it all in the same table, let's say carsTable and add a status field to the car object. Then, you can query your carsTable and filter the results on the WHERE clause using that status field.
EDIT:
From a RoomDB point of view, your query would look something like this:
#Query("SELECT * FROM carsTable WHERE status = :status")
public Car[] findCarsByStatus(String status);
you can have as much room databases for the same object as much as you want
#Database(entities = Car.class, version = 1, exportSchema = false)
public abstract class newCarsDatabase extends RoomDatabase {
private static final String LOG_TAG = newCarsDatabase.class.getSimpleName();
private static final Object LOCK = new Object();
private static newCarsDatabase sInstance;
public static newCarsDatabase getInstance(Context context) {
if (sInstance == null) {
synchronized (LOCK) {
Log.e(LOG_TAG, "creating new database");
String databaseName = "new_cars";
sInstance = Room.databaseBuilder(context.getApplicationContext(), newCarsDatabase.class, databaseName).build();
}
}
Log.e(LOG_TAG, "Getting the database instance");
return sInstance;
}
public abstract CarsDao favDao();
}
For the damaged ones
#Database(entities = Car.class, version = 1, exportSchema = false)
public abstract class demagedCarsDatabase extends RoomDatabase {
private static final String LOG_TAG = demagedCarsDatabase.class.getSimpleName();
private static final Object LOCK = new Object();
private static demagedCarsDatabase sInstance;
public static demagedCarsDatabase getInstance(Context context) {
if (sInstance == null) {
synchronized (LOCK) {
Log.e(LOG_TAG, "creating new database");
String databaseName = "damaged_cars";
sInstance = Room.databaseBuilder(context.getApplicationContext(), demagedCarsDatabase.class, databaseName).build();
}
}
Log.e(LOG_TAG, "Getting the database instance");
return sInstance;
}
public abstract CarsDao DamagedDao();
}
Also, you can simply just add data to each one you can use the same Dao to both of them or if you want you can make a Dao for each one but as much you have only one class I recommend that you make one Dao for both of them

inherited java ArrayList to JSON

I have a class that overrides ArrayList like:
public class SkmeList extends ArrayList<SkmeStatement> {
private static final long serialVersionUID = 1L;
private int skmeMajor = 0;
private int skmeMinor = 0;
private String skmeTable = null;
public void setTable(String table) {
System.out.println("Set Table: " + table);
skmeTable = table;
}
public String getTable() {
return skmeTable;
}
public void setMajor(int major) {
System.out.println("SetMajor: " + major);
skmeMajor = major;
}
public int getMajor() {
return skmeMajor;
}
public void setMinor(int minor) {
System.out.println("SetMinor: " + minor);
skmeMinor = minor;
}
public int getMinor() {
return skmeMinor;
}
}
when I attempt to write this class to a file or even a string using jackson I can only see the list contents, I do not see any of class specific attributes like Major or minor in the string/file? I treat this class just like any other java class. Is there something that is different with lists in jackson object mapper?
public void WriteJson(SkmeList statements) {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final ObjectMapper mapper = new ObjectMapper();
try {
mapper.writeValue(out, statements);
final byte[] data = out.toByteArray();
System.out.println(new String(data));
}
catch (IOException ioe) {
System.out.println("Foo");
}
}
A List has elements and no further non-element data. If you need more data, you need something that's more than a List.
The user of your class already has to treat it specially if they care about any of the extra fields you've added.
In favoring composition over inheritance, here's how I'd suggest this class could look like.
public class SkmeList {
private final int major;
private final int minor;
private final String table;
private final List<SkmeStatement> statements;
// ctor, getters, hashCode, equals and toString omitted
}
With more context on what Skme means, we could make the naming even clearer.
To make it easier to reason about, the class should be immutable, to make it safe for use in a Collection it should have hashCode() and equals(), and a toString() in case it ever gets printed/logged/debugged around.
If you don't feel like implementing all the omitted methods, consider AutoValue: you specify the getters and a factory method, the rest is generated for you.
For the user of your class, it's almost the same:
SkmeList list = ...
for (SkmeStatement stmt : list) {
...
now becomes
SkmeList list = ...
for (SkmeStatement stmt : list.getStatements()) {
...

Class Cast Exception while adding Objects to Apache Pool

I'm trying to figure out how to implement Apache Pool 2 (I'm using 2.5). As an initial POC I created an Employee Object with firstName, lastName, employeeId and age (Observer Pattern). I created an EmployeeObjectFactory which implements PooledObjectFactory and in the main class I was trying to add objects of Employee class. But I'm getting a class cast exception(EmployeeObjects cannot be cast to PooledObjects). So what changes do I need to make to my EmployeeObjects?
Employee Class
public class Employee{
private String firstName;
// omitting the getters and setters for other fields
public static class Builder {
private String firstName = "Unsub";
// declared and initialized lastName, emailId and age
public Builder firstName(String val) {
firstName = val;
return this;
}
// Similarly for other values
public EmployeeObject build() {
return new EmployeeObject(this);
}
}
private EmployeeObject(Builder builder) {
firstName = builder.firstName;
// omitting rest of the code
}
}
In the EmployeeObjectFactory
public class EmployeeObjectFactory implements PooledObjectFactory<EmployeeObject> {
#Override
public PooledObject<EmployeeObject> makeObject() {
return (PooledObject<EmployeeObject>) new EmployeeObject.Builder().build(); // This is where I'm getting the class cast
}
// Omitting rest of the code
}
Main Class
public static void main(String arg[]) throws Exception {
GenericObjectPool employeeObjectPool = new GenericObjectPool(new EmployeeObjectFactory());
employeeObjectPool.addObject();
I have tried to add as much little code as possible, because even I hate going through loads of code. Any help would be appreciated.
Finally got the answer after reading through the Apache Docs. DefaultPooledObject is what I need to use. DefaultPooledObject - "Create a new instance that wraps the provided object so that the pool can track the state of the pooled object." In the makeObject() function, I returned a DefaultPooledObject. So my code would look like
#Override
public PooledObject<EmployeeObject> makeObject() {
return new DefaultPooledObject<>(new EmployeeObject.Builder().build());
}

How to get inserted row id after inserting row in room to the main thread

I want to return the inserted row Id to use it to update some value in the same row
#Entity(tableName = "course")
public class Course {
#PrimaryKey(autoGenerate = true)
private int id;
private String firebaseId;
}
#Dao
public interface CourseDao {
#Insert(onConflict = REPLACE)
long insertCourse(Course course);
#Query("UPDATE course SET firebaseId = :firebaseId WHERE id = :id")
void updateFirebaseId(int id, String firebaseId);
}
the problem is I cant return the Id to the main thread
public class Repository {
private static final Object LOCK= new Object();
private static Repository sInstance;
private final CourseDao mCourseDao;
private final AppExecutors mAppExecutors;
public void insertCourse(final Course course) {
mAppExecutors.diskIO().execute(new Runnable() {
#Override
public void run() {
mCourseDao.insertCourse(course);
}
});
}
public void updateCourse(final Course course) {
mAppExecutors.diskIO().execute(new Runnable() {
#Override
public void run() {
mCourseDao.updateCourse(course);
}
});
}
}
I tried to use liveData but its not supported on insert
Error:(34, 20) error: Methods annotated with #Insert can return either void, long, Long, long[], Long[] or List<Long>.
Is it possible to return the id of Course once the insertion is completed without writing a separate select query?
LiveData is not supported for insert.
I feel there are 2 approaches to do insert operation in the background thread and send the result (long) back to Activity:
Using LiveData, I personally like this approach:
public class Repository {
private LiveData<Long> insertedId = MutableLiveData()
public void insertCourse(final Course course) {
mAppExecutors.diskIO().execute(new Runnable() {
#Override
public void run() {
Long id = mCourseDao.insertCourse(course);
insertId.setValue(id);
}
});
}
}
Using Rx:
return Single.fromCallable(() -> mCourseDao.insertCourse(course));
Here, you'll get Single<Long> which you can observe in your Activity
Note: Repository will return Long to ViewModel and in your ViewModel will have the LiveData and setValue stuff.
I did it like this in Java
In the environment where you want the id:
public class MyIdClass {
ArrayList<Integer> someIds = new ArrayList<>(); //this could be a primitive it doesn't matter
constructor with repository....{}
#FunctionalInterface //desugaring... normal interfaces are ok I guess?
public interface GetIdFromDao {
void getId(long id);
}
public void insertSomething(
Something something
) {
someRepository.insertSomething(
something,
id -> someIds.add((int)id) //lambda replacement, I think method reference cannot be done because of (int) casting, but if Array is type long it could be done.
);
}
}
In abstract class MyDao...: (something that I cannot stress enough..., work with ABSTRACT CLASS DAO, its more flexible)
#Insert(onConflict = OnConflictStrategy.IGNORE)
protected abstract long protectedInsertSomething(Something something);
public void insert(
Something something,
MyIdClass.GetIdFromDao getIdFromDao
) {
//get long id with insert method type long
long newSomethingId = protectedInsertSomething(something);
getIdFromDao.getId(newSomethingId);
}
Inside Repository, If you use AsyncTask<X, Y, Z>, you can actually pass everything through VarAgrs, even listeners, but be sure to recast them in the order which they get inserted, and use type Object or a common ancestor.
(Something) object[0];
(MyIdClass.GetIdFromDao) object[1];
new InsertAsyncTask<>(dao).execute(
something, //0
getIdFromDao //1
)
Also use #SupressWarnings("unchecked"), nothing happens
Now If you want even further interaction, you can connect a LiveData to the listener, or construct a Factory ViewModel...
an abstract factory ViewModel... that would be interesting.
But I believe DataBinding has an Observable view Model which I guess can be used(?)... I really don't know.

Working wiith DAO and singleton, problems with code

maybe anybody could help me out. i'm working with Data access object.
i have a database:
table Receiverz
num name
1 Walmart
2 Target
3 McDonalds
i've created a class for this table
public class Receiverz {
private int num;
private String name;
public void setNum(int num) {
this.num = num;
}
public void setName(String name) {
this.name = name;
}
}
then i created Dao interface and passed a method to it:
public interface Dao {
Receiverz getReceiverz(int num);}
Then i created a class ExpensesDao that implements Dao and created a singleton in it(i aslo set up the connection with database but i will skip that part) and overrode getReceivers(int num) method by making it possible to work with database:
public class ExpensesDao implements Dao {
private static Dao thisdao;
public static synchronized Dao getDao() {
if (thisdao==null) {
thisdao = new ExpensesDao();
}
return thisdao;
}
#Override
public Receiverz getReceiverz(int num) {
Receiverz receiver = new Receiverz();
try {
Statement stmt = myConnection.createStatement();
ResultSet result = stmt.executeQuery("SELECT * FROM receiverz");
while(result.next()){
receiver.setNum(num);
receiver.setName(result.getString(2));
}
}
catch (SQLException e){
System.out.println(e.getMessage());
}
return receiver;
}
when i try to run it in main class:
public class TryDatabase {
public static void main(String[] args) {
Dao ex = ExpensesDao.getDao();
System.out.println(ex.getReceiverz(2));
all i get is:
listexpenses.Receiverz#193499fd
but i have to get
2 Target
(since i passed 2 in the parameters and it refers to Target in my database.
does anyone know what's going wrong and what i should change in my code. P.S. i hope i made it clear enough.
ex.getReceiverz(2) is returning a Receiverz object. Thus the System.out.println(ex.getReceiverz(2)); is using the toString() method inherited from java.lang.Object. Create a toString() method in the Receiverz class that will output it the way you want.
add a getName to your recieverz and change to this ex.getReceiverz(2).getName()
A bit offtopic, but I recommend the double checked locking singleton code to avoid all concurrency issues in initalization.
Ps.: ex.getReceiverz(2).getName() breaks Law of Demeter, might be better to avoid it.

Categories