Make inherited method to use subclass' variables instead of superclass' - java

I am working on a project in Java and I decided to use MVC architecture.
I have Model class that looks like this:
public class Model {
static String table_name = null;
// ...
public static Model create(Map<String, Object> data) {
// insert `data` into database table `table_name`
}
// ...
}
I want to have class User that extends Model, but I don't want to re-implement create() in User. I just want to change value of table_name and want the inherited create() to use subclass' table_name:
public class User extends Model {
static String table_name = "users";
// ...
}
How can I achieve this?
I know a solution where I add setTableName() method to Model, but that works only if table_name is non-static. However, I need it to be static, because I want create() to be static so that I can do this: User.create().

The fact that there is a static method called 'create' on the Model class doesn't mean that there will automatically be a static 'create' method on a User class that extends the Model class - static methods belong to the class, and they're not inherited. If you implement your own 'create' method on the subclass, there's no relation between that and the 'create' method on the Model class. It sounds like you want a Factory.
If you want to stick with an approach similar to what you've done so far, something like this might work. It keeps the persistence logic in one place, while allowing each model to define its own table name and data mapping.
abstract class Model {
private PersistenceManager persistenceManager = new PersistenceManager();
abstract String getTableName();
abstract Model map(Object persistenceResult);
public void load(Map<String, Object> data) {
Object persistenceResult = persistenceManager.create(data, getTableName());
//Set appropriate fields on this class.
map(persistenceResult);
}
}
class User extends Model {
#Override
String getTableName() {
return "Users";
}
#Override
Model map(Object persistenceResult) {
//Mapping logic.
return null;
}
}
class PersistenceManager {
public Object create(Map<String, Object> data, String tableName) {
//Persistence logic.
//Return result of DB insert here.
return null;
}
}
class ModelFactory {
public static Model createModel(Class modelClass, Map<String, Object> data) {
Model model;
if (modelClass == User.class) {
model = new User();
} else {
//Cater for other models.
throw new IllegalArgumentException("Invalid model.");
}
model.load(data);
return model;
}
}
You can then use it as follows.
Map<String, Object> userData = new HashMap<>();
userData.put("name", "Bob");
Model user = ModelFactory.createModel(User.class, userData);
There is still room for cleanup in the code (like using Generics), but I'll leave that up to you.

Parent can't access child's member (and rightfully so). Look here
What you could do is created a protected method to do the task (it accepts the tablename too) and use that instead.
public class Model {
static String table_name = "A";
public static Model create(Map<String, Object> data) {
return create(data, table_name);
}
protected static Model create(Map<String, Object> data, String table_name) {
// logic here
}
}
public class User extends Model {
static String table_name = "B";
public static Model create(Map<String, Object> data) {
return create(data, table_name);
}
}

Related

Generic use of 2 same classes but from different packages

I have two same model classes,but they are in different packages.
First one is: model.my.prod.Model and second model.my.test.Model
I have also a simple mapper for this model.my.prod.Model which take a Model class as a parameter:
public class Mapper{
private static Map<Model, String> models= new HashMap<>();
static {
models.put(Model.IMAGE_JPG, MediaType.IMAGE_JPEG_VALUE);
models.put(Model.IMAGE_GIF, MediaType.IMAGE_GIF_VALUE);
}
public static String createModelMap(Model model) {
if (models.containsKey(model)) {
return models.get(model);
} else {
throw new ModelException("Exeception");
}
}
}
Now I would like to use this Mapper also for model.my.test.Model, is it possible without copy of this Mapper and change a Model package?
You can use full qualified class names. It will make your code ugly, but you will be able to use to Model classes in Mapper.
So, instead of
public static String createModelMap(Model model)
you will have two methods
public static String createModelMap(model.my.prod.Model model)
public static String createModelMap(model.my.test.Model model)
In additional I can suggest you to rename both classes with different and more meaningful names.
And, also it's a bad idea to have packages for production and testing classes, you can use default maven/gradle project structures to avoid such packages
You can use Object and explicit casting(when necessary)
public class Mapper{
// private static Map<Model, String> models= new HashMap<>();
private static Map<Object, String> models= new HashMap<>();
static {
models.put(Model.IMAGE_JPG, MediaType.IMAGE_JPEG_VALUE);
models.put(Model.IMAGE_GIF, MediaType.IMAGE_GIF_VALUE);
}
// public static String createModelMap(Model model) {
public static String createModelMap(Object model) {
if (models.containsKey(model)) {
return models.get(model);
} else {
throw new ModelException("Exeception");
}
}
}

Use of Generics during validation

I am using apache CXF.
The following API is used to post a Contact.
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
ResponseResult create(#Context HttpHeaders httpHeaders, #Context Request request, #Context UriInfo uriInfo,
UserContact contact) throws MDMException;
Here the UserContact class contains the contact information about a user which is passed as JSON in the body.
I need to do some business validations on this UserContact object. But I do not like to have too much validation code packed in a single class.
I would like to do something like the following. But I am facing issue with Generics.
interface Rule<S>
{
void applyRule(S s)throws Exception;
}
interface Validatable
{
void validate() throws Exception;
}
public class MyValidator
{
private HashMap<? extends Rule ,?> map = new HashMap<>();
public void validate() throws Exception
{
for(Rule rule : map.keySet())
{
rule.applyRule(map.get(rule));
}
}
public <S> void addRule(Rule<S> rule, S data)
{
this.map.put(rule, data);
}
}
class EMailValidationRule implements Rule<String>
{
private static final Pattern emailPattern = Pattern.compile("email-regex");
public void applyRule(String s) throws Exception
{
if(!emailPattern.matcher(s).matches())
throw new Exception("Not a valid EMail");
}
}
So the UserContact has to do the following for its validation purposes. This keeps the code compact (IMO).
class UserContact implements Validatable
{
// some
// code
// related to User Contact
public void validate() throws Exception
{
MyValidator validator = new MyValidator();
validator.addRule(new EMailValidationRule(), "developer#stackoverflow.com");
validator.addRule(new PhoneValidationRule(), "+1234567890");
validator.validate();
}
}
I keep getting error like :
The method put(capture#5-of ? extends Rule, capture#6-of ?) in the type HashMap is not applicable
for the arguments (Rule, S)
Also is the above design good for doing validations?
The problem is that, although your encapsulation ensures it, the compiler can not be sure that the retrieved Rule<...> has a type argument of the same type as the retrieved data.
There is also the problem of not being able to insert a Rule<T> with data of a subtype of T. If you have Rule<S> rule, S data the types have to be an exact match. While a Rule<S> could handle a subtype of S just fine.
While MyValidator is a cool little class, I can't really see the point in having it. Especially because you create a new one every time you call validate. It would also be hard to cache because the rules are static (the same for every instance of the class) and the data comes from individual instances (I'd assume).
You could also just do this:
class UserContact implements Validatable
{
// some
// code
// related to User Contact
// 1 rule instance for the entire class, not a new one per call to 'validate'
private static EMailValidationRule emailRule = new EmailValidationRule();
private static PhoneValidationRule phoneRule = new PhoneValidationRule();
public void validate() throws Exception
{
emailRule.applyRule("developer#stackoverflow.com");
phoneRule.applyRule("+1234567890");
}
}
Never the less, here is a working version of MyValidator:
class MyValidator {
private Map<Rule<?>, RuleNode<?>> map = new HashMap<>();
public void validate() throws Exception {
for(RuleNode<?> node : map.values())
node.apply();
}
public <T, D extends T> void addRule(Rule<T> rule, D data) {
#SuppressWarnings("unchecked") // unchecked, but safe due to encapsulation
RuleNode<T> r = (RuleNode<T>) map.get(rule);
if(r == null) {
r = new RuleNode<T>(rule);
map.put(rule, r);
}
r.add(data);
}
private static class RuleNode<T> { // Maintains that the rule and data are compatible
private final Rule<T> rule;
private final List<T> list = new ArrayList<>();
public RuleNode(Rule<T> rule) {
this.rule = rule;
}
public void add(T data) {
list.add(data);
}
public void apply() throws Exception {
for(T data : list)
rule.applyRule(data);
}
}
}
You just need to make a generic Version of the MyValidator Class
A generic class is defined with thss format:
class name<T1, T2, ..., Tn> { /* ... */ }
Defining the class using generics you will specify the types you want to use in your class, in your case <R extends Rule<S> ,S>
class MyValidator<R extends Rule<S> ,S>{
private HashMap<R ,S> map = new HashMap<>();
public void validate() throws Exception{
for(Rule<S> rule : map.keySet()){
rule.applyRule(map.get(rule));
}
}
public void addRule(R rule, S data){
this.map.put(rule, data);
}
}
Once done you just have to build a MyValidator of the desired type :
MyValidator<Rule<String>, String> validator = new MyValidator<>();
And finally add the rules matching the types of the validator :
validator.addRule(new EMailValidationRule(), "developer#stackoverflow.com");
So for example addind a phone validator your UserContact will looks like :
class PhoneValidationRule implements Rule<String>{
private static final Pattern phonePattern = Pattern.compile("phone-regex");
public void applyRule(String s) throws Exception{
if(!phonePattern.matcher(s).matches())
throw new Exception("Not a valid phone");
}
}
class UserContact implements Validatable{
public void validate() throws Exception{
MyValidator<Rule<String>, String> validator = new MyValidator<>();
validator.addRule(new EMailValidationRule(), "developer#stackoverflow.com");
validator.addRule(new PhoneValidationRule(), "developer#stackoverflow.com");
validator.validate();
}
}

Play 2.5.x java - How to call a controller method from another controller?

I have two controllers A and B. I want to call A's create method from B. How do I do that?
public class A extends Controller {
public Result create() {
...
}
}
public class B extends Controller {
public Result create() {
// How to call A.create() ??
}
}
In Play 2.5, since DI (Google Guice) is in-built, we can inject the desired controller and call its methods straightaway!
public class A extends Controller {
public Result create(String param) {
...
}
}
public class B extends Controller {
#Inject
private A a;
public Result create() {
a.create("param");
// do other stuff and return result
}
}
I believe that you need to define another method to contain your logic and then call that method from your controller's method. something like this:
public class SampleController extends Controller{
#BodyParser.Of(BodyParser.Json.class)
public Result createMessage(){
JsonNode json = request().body().asJson();
return ok(createMessageLogic(json.get("id").asLong()));
}
public String createMessageLogic(long id){
return "the id is " + id;
}
}
This way, you can easily inject your controller in the other controller and call the logic methods. However you can consider having a service layer aside your controller classes which is way cleaner.
Just create static method that shares the logic and use it to build the Result for both actions it will contain request() automatically, simplest sample:
public class A extends Controller {
public Result create(String param) {
return shared("A role", param);
}
public static Result shared(String role, String param) {
String msg = "Path: " + request().path();
msg += ", role is: `" + role + "`";
msg += (param == null)
? " and has no param"
: ", with param: " + param;
return ok(msg);
}
}
public class B extends Controller {
public Result create() {
return controllers.A.shared("B role", null);
}
}
About params
Note that actually the only params that you need to resolve in A.create(...) and B.create(...) are the route parts (String param in the A route) or maybe some optional params hardcoded in each like role in the example.
Other things, like request, session, cache, DB data, etc. may (should) be extracted within the shared() method.
TIP: if number of params required to pass to the shared() method is large and you do not want to create monsters like
controllers.A.shared(null, null, null, null, null, null, null, null, "foo");
just create Map<String, Object> collection and pass all at once OR create dedicated transient model, that holds all values in proper fields. The second option is my favorite also when passing many values to common Twirl's templates.

How to specify object custom serialization in ORMLite?

I would like to store some field of type ParentClass as json string into my database. I don't want to use Serializable interface and DataType.SERIALIZABLE cause it ties with full class name of serialized class.
So I'm using the following code:
class ParentClass {
#DatabaseField(persisterClass = MyFieldClassPersister.class)
private MyFieldClass myField;
}
where persister class a kind of:
public class MyFieldClassPersister extends StringType {
private static final MyFieldClassPersister singleTon = new MyFieldClassPersister();
public static MyFieldClassPersister getSingleton() {
return singleTon;
}
protected MyFieldClassPersister() {
super(SqlType.STRING, new Class<?>[0]);
}
#Override
public Object parseDefaultString(FieldType fieldType, String defaultStr) {
return jsonStringToObject(defaultStr);
}
#Override
public Object resultToSqlArg(FieldType fieldType, DatabaseResults results, int columnPos) throws SQLException {
String string = results.getString(columnPos);
return jsonStringToObject(string);
}
private static MyFieldClass jsonStringToObject(String string) {
// json to object conversion logic
}
}
Here are two issues I've met:
I didn't get how to specify custom convertion from object to string. Seems that ORMLite calls Object.toString() in order to get string representation of the object. It would be great to have some method in Persister in which I could specify how to convert Object to string (json in my case). Yes, I can override toString() method in MyFieldClass, but it is more convenient to perform conversion in Persister. Is there any method I could override in order to specify convertion from model object to db-object?
If I mark my custom field type as String type:
class ParentClass {
#DatabaseField(dataType = DataType.STRING, persisterClass = MyFieldClassPersister.class)
private MyFieldClass myField;
}
then ormlite crashes when saving object with the following message:
java.lang.IllegalArgumentException: Field class com.myapp.venue.MyFieldClass for
field FieldType:name=myField,class=ParentClass is not valid for type
com.j256.ormlite.field.types.StringType#272ed83b, maybe should be
class java.lang.String
It doesn't crash if I omit dataType specification. Can I avoid this crash in some way? It seems to me that it's better to specify types explicitly.
So basically your persister should be implemented in the next way:
public class MyFieldClassPersister extends StringType {
private static final MyFieldClassPersister INSTANCE = new MyFieldClassPersister();
private MyFieldClassPersister() {
super(SqlType.STRING, new Class<?>[] { MyFieldClass.class });
}
public static MyFieldClassPersister getSingleton() {
return INSTANCE;
}
#Override
public Object javaToSqlArg(FieldType fieldType, Object javaObject) {
MyFieldClass myFieldClass = (MyFieldClass) javaObject;
return myFieldClass != null ? getJsonFromMyFieldClass(myFieldClass) : null;
}
#Override
public Object sqlArgToJava(FieldType fieldType, Object sqlArg, int columnPos) {
return sqlArg != null ? getMyFieldClassFromJson((String) sqlArg) : null;
}
private String getJsonFromMyFieldClass(MyFieldClass myFieldClass) {
// logic here
}
private MyFieldClass getMyFieldClassFromJson(String json) {
// logic here
}
}
You should register it in onCreate method of your OrmLiteSqliteOpenHelper class
#Override
public void onCreate(SQLiteDatabaseHolder holder, ConnectionSource connectionSource) {
try {
//...
DataPersisterManager
.registerDataPersisters(MyFieldClassPersister.getSingleton());
} catch (SQLException e) {
// log exception
}
}
And then you can use it in your model like this:
#DatabaseField(persisterClass = MyFieldClassPersister.class, columnName = "column_name")
protected MyFieldClass myFieldClass;
Don't register the persister adapter in the onCreate() method. This method only gets called when your database is first created. You should add this somewhere else, like your constructor or onOpen() method.

More flexible factory

I'm currently working on a little project for school. In my Java application I need a database, and I would like to make it possible to make my application capable to use different types of databases. So I currently implemented a txt-database and a PostgreSQL. In the future, it should be possible to add other database types. Like XML or MySQL, ...
To create a database instance, I designed a factory that uses an enum. It works perfectly, but Itsn't really flexibel in my opionio. So, I did some research, but didn't find a real good example that is clear for me.
This is my enum:
public enum DatabaseType {
TXT,
SQL,
XML;
}
This is my factory:
public class DatabaseFactory {
public Database createDatabase(DatabaseType type, String databaseName) throws DatabaseException {
if(type.equals(DatabaseType.TXT)) {
return new FileDatabase(databaseName);
}else if(type.equals(DatabaseType.SQL)) {
return new SQLDatabase(databaseName);
}else if(type.equals(DatabaseType.XML)) {
return new XMLDatabase(databaseName);
}else {
//default
return new FileDatabase(databaseName);
}
}
}
My aim is to only edit the enum in the future, without touching the factory itself. This should give me enough flexibility, but I've no idea how I could do this.
You could put the factories in the enum itself.
public enum DatabaseType {
TXT {
#Override
public Database createDatabase(String databaseName) {
return new FileDatabase(databaseName);
}
},
SQL {
#Override
public Database createDatabase(String databaseName) {
return new SQLDatabase(databaseName);
}
},
XML {
#Override
public Database createDatabase(String databaseName) {
return new XMLDatabase(databaseName);
}
};
public abstract Database createDatabase(String databaseName);
}
In Java, enums are not just nice names for integral values (like in C). A better way to think of an enum is as a class with a fixed number of instances. Together with the concept of anonymous classes, you can give each value in the enumeration different properties and methods specific for that value.
Use reflection:
Your enum:
public enum DatabaseType {
FILE(FileDatabase.class),
SQL(SQLDatabase.class);
private Database db;
DatabaseType(Class<Database> db) {
this.db = db;
}
/*package friendly*/ Class<Database> getDatabase() {
return this.db;
}
}
Your factory:
public class DatabaseFactory {
public static Database create(DatabaseType type, String dbName) throws Exception {
Database db = null;
Constructor cons = type.getDatabase().getDeclaredConstructor(new Class[] { String.class });
cons.setAccessible(true);
db = cons.newInstance(dbName);
return db;
}
}
Your Database implementors:
public class FileDatabase extends Database {
/* can only be instantiated via reflection */
private FileDatabase(String databaseName) {
// init db.
}
}

Categories