Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properties with underscores in names are not found #3

Closed
plandem opened this issue May 4, 2015 · 11 comments
Closed

Properties with underscores in names are not found #3

plandem opened this issue May 4, 2015 · 11 comments
Assignees
Milestone

Comments

@plandem
Copy link

plandem commented May 4, 2015

if at DB side i have int4/int8 and at Model side i have Integer, than DefaultInstantiatorRegistry can't instantiate the class. Postgres.

@komu
Copy link
Member

komu commented May 4, 2015

That sounds weird and is definitely something that should be supported. Do you have a matching constructor or a default constructor and public field/setter for the field?

Can you post a minimal failing example (class and call to the query)? What version of Dalesbred are you using?

Anyway, it seems that at least I need to improve the error messages.

@komu komu self-assigned this May 4, 2015
@plandem
Copy link
Author

plandem commented May 4, 2015

that's all code actually

        try {
                postgres = Database.forDataSource(DataSourceFactory.getDataSourceFromProperties("db-bi"));
            List<Location> rows = postgres.findAll(Location.class, Location.prototype.findAll(false));
        } catch (Exception e) {
            logger.fatal(e.toString());
            e.printStackTrace();
        }

that's model

public class Location extends Model {
    public static final Model prototype =  Model.getPrototype(MethodHandles.lookup().lookupClass());

    public Integer loc_id;
//    public Integer parent_id;
//    public Double longitude;
//    public Double latitude;
//    public Integer level;
//    public Date created;
//    public Date input_time;
//    public Integer is_point;
    public String title;

    @Override
    public String tableName() {
        return "locations";
    }

    @Override
    public String primaryKeyName() {
        return "loc_id";
    }
}

and at DB side (postgres), loc_id is int4

@plandem
Copy link
Author

plandem commented May 4, 2015

and my base Model class is a wrapper without properties or getters/setters. just with some additional default methods like 'primaryKeyName()', 'tableName()' and etc.

@plandem
Copy link
Author

plandem commented May 4, 2015

abstract public class Model<T> {
    private static final Map<Class<?>, Object> prototypesCache = new ConcurrentHashMap<>();
    private static final Map<Class<?>, ArrayList<String>> attributesCache = new ConcurrentHashMap<>();
    private static final Map<Class<?>, ArrayList<Field>> fieldsCache = new ConcurrentHashMap<>();

    /**
     * Create, cache and return singleton for provided class
     * @param type
     * @return Model
     */
    static synchronized protected Model getPrototype(Class type) {
        if (type == null || type.getSuperclass() != Model.class) {
            throw new NullPointerException("Type is null or not subclass of " + Model.class.getSimpleName());
        }

        Object model = null;
        if(!(prototypesCache.containsKey(type))) {
            try {
                model = type.newInstance();
                prototypesCache.put(type, model);
            } catch (Exception ignored) {
                throw new NullPointerException("Can't create singleton for  " + Model.class.getSimpleName());
            }
        } else {
            model = prototypesCache.get(type);
        }

        return (Model)model;
    }

    protected synchronized ArrayList<Field> fields() {
        Class cls = this.getClass();

        if(fieldsCache.containsKey(cls))
            return fieldsCache.get(cls);

        ArrayList<Field> fields = new ArrayList<>();
        try {
            for (Field f : cls.getDeclaredFields()) {
                if(!(Modifier.isStatic(f.getModifiers()))) {
                    fields.add(f);
                }
            }
        } catch(Exception ignored) {

        }

        fieldsCache.put(cls, fields);
        return fields;
    }

 /**
     * Return list of attributes for model
     * @return ArrayList<String>
     */
    public synchronized ArrayList<String> attributeNames() {
        Class cls = this.getClass();

        if(attributesCache.containsKey(cls))
            return attributesCache.get(cls);

        ArrayList<String> attributes = new ArrayList<>();
        try {
            attributes.addAll(fields().stream().map(Field::getName).collect(Collectors.toList()));
        } catch(Exception ignored) {

        }

        attributesCache.put(cls, attributes);
        return attributes;
    }

    /**
     * Return name of table
     * @return String
     */
    public String tableName() {
        return this.getClass().getSimpleName().toLowerCase();
    }

    /**
     * Return name of primary key
     * @return String
     */
    public String primaryKeyName() {
        if (!(this.attributeNames().contains("id"))) {
            throw new NullPointerException("There is no primary key with name ID. You must override primaryKeyName() for that model.");
        }

        return "id";
    }
    /**
     * return SQL query to get list of all models
     * @return String
     */
    public String findAll(String field, boolean sort) {
        String sql = "SELECT " + StringUtils.join(this.attributeNames(), ",") + " FROM " + this.tableName();

        if(sort)
            sql += " ORDER BY " + field + " ASC";

        System.out.println(sql);
        return sql;
    }

    public String findAll(boolean sort) {
        return findAll(this.primaryKeyName(), sort);
    }

    public String findAll() {
        return findAll(false);
    }
}

@plandem
Copy link
Author

plandem commented May 4, 2015

i'm not very pro at java, just few days, but for me looks like nothing exceptional here. At least other models with another connection (to Oracle) works fine. Only Postgres with all these int4, int8 failed :(

@plandem
Copy link
Author

plandem commented May 4, 2015

version is:
compile group: 'fi.evident.dalesbred', name: 'dalesbred', version: '0.8.0'

@komu
Copy link
Member

komu commented May 4, 2015

The problem is actually very simple: your property in Java is named loc_id instead of locId. Dalesbred assumes that you use normal camelCase-naming convention for Java-properties and strips underscores from the database column name before performing a case-insensitive lookup to find field or setter.

So just rename the property to locId and it should work.

That said, the error message should have been more informative. And actually there's no reason for Dalesbred to enforce not having underscores in Java properties. It's not normal style, but if you want or need them, Dalesbred should still find the property.

So I'm leaving this issue open and will improve the property-resolution algorithm for next version so that it would have find your loc_id as well.

@komu komu added this to the 1.0 milestone May 4, 2015
@komu komu changed the title "could not find a way to instantiate class" Properties with underscores in names are not found May 4, 2015
@plandem
Copy link
Author

plandem commented May 4, 2015

at db side field named as loc_id. i see solution for right now (strip underscore at Java side), but it would be better to allow to use underscore. i would like to stick the same names as at db.

@plandem
Copy link
Author

plandem commented May 4, 2015

hmmm...actually at my current way of usage it does not help :( because i'm trying to build query from public properties of model :( if i remove underscore, than final sql would be corrupted :(

@komu
Copy link
Member

komu commented May 4, 2015

Well, your SQL-generation code could convert fooBar to foo_bar, right? Use something like Dalesbred's StringUtils.upperCamelToLowerUnderscore(). (The utils are not part of Dalesbred's public API though and are moved under org.dalesbred.internal-package in 1.0 -branch, but as a temporary fix you can probably depend on it directly, or just copy-paste the code.)

But I will fix this in a future version anyway.

@plandem
Copy link
Author

plandem commented May 4, 2015

yep, finally i came to same solution as you advice - just used your StringUtils. As tempo solutions it's enough (i wrote a really simple 'query builder' with using your StringUtils).

@komu komu closed this as completed in 8d380c8 May 4, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants