I understand, thanks to this question, that the value of a static field declared in an abstract class will be the same among all subclasses.
The solution in the aforementioned question is to declare a static field in each subclass, and an abstract "getter" instance method in the abstract class that must be implemented by each subclass.
But I have a static method in my abstract class, and I need to refer to the static field of the subclass. I can't do this because the getter is an instance method.
What's the best solution here? I'd rather not put nearly identical instances of getAll in every subclass.
public abstract class AbstractModel {
public abstract String getTableName();
public static ResultSet getAll() {
Statement stmt = Database.get().conn.createStatement();
// Error below: Cannot use "this" in static context.
String query = "SELECT * FROM `" + this.getTableName() + "`";
return stmt.executeQuery(query);
}
}
public class Api extends AbstractModel {
protected static final String TABLE_NAME = "apis";
#Override
public String getTableName() {
return TABLE_NAME;
}
}
I was able to write the code in this way, to minimize repitition. It also eliminates the need for a getter.
public abstract class AbstractModel {
public static ResultSet getAllFromTable(String tableName) {
Statement stmt = Database.get().conn.createStatement();
String query = "SELECT * FROM `" + tableName + "`";
return stmt.executeQuery(query);
}
}
public class Api extends AbstractModel {
protected static final String TABLE_NAME = "apis";
public static ResultSet getAll() {
return getAllFromTable(TABLE_NAME);
}
}
Related
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";
}
I'm coming from PHP and moved to java. I'm asking myself (and you guys) if there is a way to implement someting like this:
I'm trying to implement a class/classes to create CRUD operations for many database entities. All entities inherit their functions (most of them from the parent)
I need to implement the tableName and idFieldName in the parent class DatabaseEntity to avoid compiler warnings.
It seems like java tries to use the parents properties (which are obviously null) because the function is implemented in the parent.
Is there a way to overcome this problem? Any feedback is greatly apreciated!
abstract class DatabaseEntity {
protected String tableName;
protected String idFieldName;
public DataRecord readFromDB(int recordID) throws SQLException {
...
String sqlStatement = String.format("SELECT * FROM %s WHERE %s = %s", this.tableName, this.idFieldName, recordID); // Exception shows this line
...
}
}
class DatabaseRecord extends DatabaseEntity {
protected String tableName = "DatabaseRecordTable";
protected String idFieldName = "ID";
public void getRecord() {
...
DataRecord record = this.readFromDB(1); // leads to java.lang.NullPointerException: null
...
}
}
Disclaimer: I'm new to github and I apreciate any feedback on improoving my posts :)
when you use the method readFromDBin which you refer to the tableNameand the idFieldName, these two fields remain nullas long as they are not initailized,
try to remove the fields from your abstract class and do something like this :
abstract class DatabaseEntity {
// protected String tableName;
// protected String idFieldName;
public abstract String getTableName();
public abstract String getIdFieldName() ;
public DataRecord readFromDB(int recordID) throws SQLException {
...
String sqlStatement = String.format("SELECT * FROM %s WHERE %s = %s",getTableName(), getIdFieldName(), recordID);
...
}
}
and the implementation would be like :
class DatabaseRecord extends DatabaseEntity {
protected String tableName = "DatabaseRecordTable";
protected String idFieldName = "ID";
public void getRecord() throws SQLException {
DataRecord record = this.readFromDB(1);
}
#Override
public String getTableName() {
return this.tableName;
}
#Override
public String getIdFieldName() {
return this.idFieldName ;
}
}
Currently I have several enums defined over several classes. They all look similar to the one shown below:
public class ApaMessage {
private String apaMessage;
private final int FIXED_LENGTH_SIZE=39;
public enum ApaFields {
FIELD1(ApaUtils.ApaFieldTypes.POSITION_BASED, null, "field1", 2, 3, false, false),
private final ApaUtils.ApaFieldTypes type;
private final String ApaName;
private final String jsonName;
private final int start;
private final int finish;
private boolean required = false;
private boolean withDelimiter = false;
ApaFields(ApaUtils.ApaFieldTypes type, String ApaName, String jsonName, int start, int finish, boolean required, boolean withDelimiter) {
this.type = type;
this.ApaName = ApaName;
this.jsonName = jsonName;
this.start = start;
this.finish = finish;
this.required = required;
this.withDelimiter = withDelimiter;
}
}
There is also a method defined in ApaMessage:
private HashMap<String,Object> getApaJsonFieldsAndValues() {
HashMap<String, Object> jsonApaData = new HashMap<String, Object>();
for (ApaFields field : ApaFields.values()) {
jsonApaData.put(field.jsonName, getApaFieldValue(field));
}
return jsonApaData;
}
The problem is although there isn't a lot of code, I will soon have 10-20 of these enums. I would like to create an abstract base class where the HashMap method, and other similar methods can be part of. The base class should accept an ApaFields enum and other enums and do what the getApaJsonFieldsAndValues does. The problem is, how can the base class access the passed enum values and the internal fields such as jsonName to do the loop?
I have tried different approaches but the main problem is that the base class cannot seem to access the values. Is there any way around this? Alternatively, is there a better approach? Thanks
EDIT:
Basically I would like something like this in the base class. Note the below doesn't compile.
public abstract class ApaRequestMessage {
private Class<? extends Enum<?>> apaRequestMessageFields;
private String apaMessage;
public <T extends Enum<T>> void ApaRequest(Object apaRequestFields, String apaMessage) {
apaRequestMessageFields = (Class<? extends Enum<?>>) apaRequestFields;
this.apaMessage = apaMessage;
for (Field field: apaRequestMessageFields.values()) {
//this doesn't work because it cannot access the values of apaRequestMessageFields
}
}
}
And then call the base method as follows, although not sure if this is correct, where ApaFields is the inner enum defined above.
ApaRequest(ApaFields.class, somestringmessage);
I came across something similar when trying to define a db schema using enums as columns in the table. I eventually took this route.
Define a base class with sufficient generic signature to ensure the enum is properly built.
public class Table<Column extends Enum<? extends Column>> {
// Name of the table.
protected final String tableName;
// All of the columns in the table. This is actually an EnumSet so very efficient.
protected final Set<Column> columns;
/**
* The base interface for all Column enums.
*/
public interface Columns {
// What type does it have in the database?
public Type getType();
}
// Small list of database types.
public enum Type {
String, Number, Date;
}
public Table(String tableName,
Set<Column> columns) {
this.tableName = tableName;
this.columns = columns;
}
}
Now extend this for each table - here is a simple VersionTable:
public class VersionTable extends Table<VersionTable.Column> {
public enum Column implements Table.Columns {
Version(Table.Type.String),
ReleaseDate(Table.Type.Date);
final Table.Type type;
Column(Table.Type type) {
this.type = type;
}
#Override
public Type getType() {
return type;
}
}
public VersionTable() {
super("Versions", EnumSet.allOf(Column.class));
}
}
Now you have all of the core functionality in the base class and all the sub-classes need to do is implement the interface on the enum.
I realise this does not address the issue of duplicated bolierplate code in all of your enums but it does move alomst all of it elsewhere.
interface STD{
public Name getName();
}
class Student implements STD{
Name getName(){ }
private class Name{
}
}
In the code above the interface can't see the private class Name, is there a way to make it see it while it's a private inner class to define a data type from it?
protected class Name
Use a protected variable so the interface can see it, but not any other class that isn't directly related
You don't want to do that. private is meant to be.. private.
What you probably want to do is declaring Name outside Student (it make sense, Name entity shouldn't be tied only to students):
public class Student implements STD {
public Name getName() {
// ...
}
}
interface STD {
public Name getName();
}
class Name { }
Note that you can have Name in a separate file, it's up to you and your needs where to place it.
This will work for you :
package test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
interface STD{
public Object getName();
}
class Student implements STD {
#Override
public Name getName() {
return null;
}
private class Name {
private int myField = 5;
}
public static void main(String[] args) {
try {
Student outer = new Student();
// List all available constructors.
// We must use the method getDeclaredConstructors() instead
// of getConstructors() to get also private constructors.
for (Constructor<?> ctor : Student.Name.class
.getDeclaredConstructors()) {
System.out.println(ctor);
}
// Try to get the constructor with the expected signature.
Constructor<Name> ctor = Student.Name.class.getDeclaredConstructor(Student.class);
// This forces the security manager to allow a call
ctor.setAccessible(true);
// the call
Student.Name inner = ctor.newInstance(outer);
System.out.println(inner);
Field privateField = Class.forName("test.Student$Name").getDeclaredField("myField");
//turning off access check with below method call
privateField.setAccessible(true);
System.out.println(privateField.get(inner)); // prints "5"
privateField.set(inner, 20);
System.out.println(privateField.get(inner)); //prints "20"
} catch (Exception e) {
System.out.println("ex : " + e);
}
}
}
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";
}