Метод подстановки постоянных объектов на примере объекта


на данный момент я пишу родовой объект доступа к данным для персистентных сущностей мне нужно разобраться в моем приложении в JavaEE. Что это codeReview о метод findByExample, который используется для поиска записи в базе данных, на основе данного примера объекта. Это уже может быть реализован за объектом openjpa, но я обязан объектом openjpa снимка 1.2.х (старый) и я не мог найти его в любом месте в ручную. пожалуйста, расскажите про подводные камни, улучшения и плохие практики, которые вы видите в моем коде. Я готов учиться.

Здесь мы идем

Использовать его следующим образом

MyEntity me = new MyEntity();
me.setName("john")
me.setAge(18);

entityAccess.findByExample(me);

создадим следующий запрос на JPQL, используя только те параметры, которые были установлены

"SELECT x FROM MyEntity x WHERE x.name = :name AND x.age = :age"

Тогда он будет задавать параметры и exequte запрос.

Вот как я реализовал это.

Двух публичных методов (BasicEntity является mappedSuperClass)

public <T extends BasicEntity> List<T> findAllByExample(T entity) {
return getQueryFromExample(entity).getResultList();
}

public <T extends BasicEntity> T findByExample(T entity) throws NoResultException, NonUniqueResultException {
return (T) getQueryFromExample(entity).getSingleResult();
}

Метод это создает запрос и наборов параметров. (ЭМ СПД через EntityManager)

private <T extends BasicEntity> Query getQueryFromExample(T entity) {

HashMap<String, Object> fieldNameValuePairs = getFieldNameValuePairs(entity.getClass(), entity);

/* build query string */
StringBuilder queryString = new StringBuilder();
queryString.append("SELECT x FROM ");
queryString.append(entity.getClass().getSimpleName());
queryString.append(" x WHERE ");
for (Iterator<String> iterator = fieldNameValuePairs.keySet().iterator(); iterator.hasNext();) {
    String fieldName = iterator.next();
    queryString.append("x." + fieldName + " = :" + fieldName);
    if (iterator.hasNext()) {
    queryString.append(" AND ");
    }
}


/* create query and set parameters */
Query q = em.createQuery(queryString.toString());
for (Entry<String, Object> entry : fieldNameValuePairs.entrySet()) {
    String fieldName = entry.getKey();
    Object fieldValue = entry.getValue();
    q.setParameter(fieldName, fieldValue);
}

return q;
}

Следующий метод добавляет значимым и не нулевые поля в HashMap. Он поднимается по иерархии типа с рекурсией. Объект.getClass().getSuperClass возвращает значение null.

private HashMap<String, Object> getFieldNameValuePairs(Class<?> type, Object instance) {

if (type != null) {
    HashMap<String, Object> fieldNameValuePairs = new HashMap<String, Object>();

    for (Field field : type.getDeclaredFields()) {

    /* we only care for persitent fields */
    if (isPersistentField(field)) {
        field.setAccessible(true);
        String fieldName = field.getName();
        Object fieldValue = null;
        try {
        fieldValue = field.get(instance);
        } catch (Exception e) {
        throw new RuntimeException(
            "Error reflecting fields while building queryString for findByExample. Cannot read "
                + instance.getClass().getSimpleName() + "." + fieldName, e);
        }

        /* ignore unset fields */
        if (!(fieldValue == null || fieldValue.equals(""))) {
        fieldNameValuePairs.put(fieldName, fieldValue);
        System.out.println(fieldName + " = " + fieldValue.toString());
        }
    }

    }

    /* recursive step up the type hierarchy */
    fieldNameValuePairs.putAll(getFieldNameValuePairs(type.getSuperclass(), instance));
    return fieldNameValuePairs;
} else {
    return new HashMap<String, Object>();
}
}

следующий фрагмент кода определяет, является ли поле актуальна (так как я не хочу, например serialVersionUID в моем JPQL запроса) Я решил использовать аннотации полей, чтобы решить, потому что я думал, что это делает сохраняются поля уникальным.

private static final List<Class<? extends Annotation>> persistenceAnnotations = new ArrayList<Class<? extends Annotation>>();
static {
persistenceAnnotations.add(Id.class);
persistenceAnnotations.add(Column.class);
persistenceAnnotations.add(JoinColumn.class);
persistenceAnnotations.add(OneToOne.class);
persistenceAnnotations.add(ManyToOne.class);
persistenceAnnotations.add(OneToMany.class);
persistenceAnnotations.add(ManyToMany.class);
persistenceAnnotations.add(JoinColumns.class);
persistenceAnnotations.add(Basic.class);
persistenceAnnotations.add(Lob.class);
}

private boolean isPersistentField(Field field) {
for (Annotation annotation : field.getAnnotations()) {
    if (persistenceAnnotations.contains(annotation.annotationType())) {
    return true;
    }
}
return false;
}

Aug29 - рефакторинг версии

private <T extends BasicEntity> Query getQueryFromExample(T entity) {
    HashMap<String, Object> fieldNameValuePairs = getFieldNameValuePairs(entity.getClass(), entity);

    /* build query string */
    String queryString = String.format("SELECT x FROM %s WHERE %s", entity.getClass().getSimpleName(),
            join(asJpqlConditions(fieldNameValuePairs.keySet(), "x"), " AND "));

    /* create query and set parameters */
    Query q = em.createQuery(queryString.toString());
    for (Entry<String, Object> entry : fieldNameValuePairs.entrySet()) {
        q.setParameter(entry.getKey(), entry.getValue());
    }

    return q;
}

private List<String> asJpqlConditions(Collection<String> fieldNames, String identifier) {
    List<String> conditions = new ArrayList<String>();
    for (String fieldName : fieldNames) {
        conditions.add(String.format("%1$.%2$s = :%2$s", identifier, fieldName));
    }
    return conditions;
}

private String join(List<String> parts, String glue) {
    StringBuilder sb = new StringBuilder();
    boolean first = true;
    for (String part : parts) {
        if (!first) {
            sb.append(glue);
        }
        sb.append(part);
        first = false;
    }
    return sb.toString();
}

private HashMap<String, Object> getFieldNameValuePairs(Class<?> type, Object instance) {

    /* return empty HashMap in last recursion step (type is null) */
    if (type == null) {
        return new HashMap<String, Object>();
    }

    /* recursive call to fill the HashMap with the fields from supertypes */
    HashMap<String, Object> fieldNameValuePairs = getFieldNameValuePairs(type.getSuperclass(), instance);

    for (Field field : type.getDeclaredFields()) {

        /* we only care for persitent fields */
        if (!isPersistentField(field)) {
            continue;
        }

        boolean wasAccessible = field.isAccessible();
        field.setAccessible(true);
        String fieldName = field.getName();
        Object fieldValue = null;
        try {
            fieldValue = field.get(instance);
        } catch (Exception e) {
            throw new RuntimeException("Error reflecting fields while building queryString for findByExample. Cannot read "
                    + instance.getClass().getSimpleName() + "." + fieldName, e);
        }
        field.setAccessible(wasAccessible);

        /* ignore unset fields */
        if (!(fieldValue == null || fieldValue.equals(""))) {
            fieldNameValuePairs.put(fieldName, fieldValue);
        }
    }

    return fieldNameValuePairs;
}

private boolean isPersistentField(Field field) {
    for (Annotation annotation : field.getAnnotations()) {
        if (annotation.annotationType().equals(PersistentField.class)) { //added custom marker annotation
            return true;
        }
    }
    return false;
}


423
3
задан 22 августа 2011 в 07:08 Источник Поделиться
Комментарии
1 ответ

Я нашел код интересно читать и довольно приятное на этом. Ничего критичного выскочили думал, что есть какая-то проверка, условие, предоставление, стиль и другие нарушения в отношении того, что я мог бы сделать.

1) Мне нравится аннотация тривиальные вещи. Например, когда вы создаете строку запроса, я хотел бы попробовать, чтобы подчеркнуть структуру строке запроса вместо шума, вызванного его дом. Это может быть сделано путем создания метод(ы), которые создают условие WHERE запроса:

import static java.lang.String.format; 

...

String queryString = format("SELECT x FROM %s WHERE %s",
entity.getClass().getSimpleName(),
join(asConditions(fieldNameValuePairs.keySet()), " AND "));

...

private Collection<String> asConditions(Collection<String> fieldNames) {
List<String> conditions = new ArrayList<String>();
for(String fieldName : fieldNames) {
conditions.add(format("x.%1$s = :%1$s", fieldName)); // 1$ reuses the 1st argument
}
return conditions;
}

private String join(Collection<String> parts, String glue) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for(String part : parts) {
if(!first) {
sb.append(glue);
}
sb.append(part);
first = false;
}
return sb.toString();
}

2) я считаю, что назначения не нужны здесь, так как они создают новые линии коды не повысит удобочитаемость целом.

for (Entry<String, Object> entry : fieldNameValuePairs.entrySet()) {
String fieldName = entry.getKey();
Object fieldValue = entry.getValue();
q.setParameter(fieldName, fieldValue);
}

=>

for (Entry<String, Object> entry : fieldNameValuePairs.entrySet()) {
q.setParameter(entry.getKey(), entry.getValue());
}

3) во втором способе, вы создали долго, если блок в CAS тип имеет значение null. Я бы не рано, поэтому предпосылки метода показана в начале. Также я считаю, что если условие не выполняется, то какая-то регистрация нужна или еще лучше будет выброшено исключение, потому что я думаю, что отсутствует тип ошибка программиста некоторых видов и не следует упускать путем.

Если нулевой чек из-за рекурсии, вы, возможно, захотите, чтобы создать комментарий, как рекурсия шаг является немного далеко от разветвления заявление. Еще лучше поставить рекурсии в один метод и логика для рекурсии шаг в другом.

4) заметив, что второй способ является частным, я заметил, что нет проверки условия в первом способе. Если условия будут проверены, вы можете быть немного более расслабленным с проверкой в частные методы хотя частные методы могут в конечном итоге использоваться другим разработчиком в какой-то момент.

5) Снова при ветвлении на isPersistentField() вы создаете большой блок вместо выхода быстро. Если вы предпочли использовать этот стиль, то я бы посоветовал вам поставить содержимое блока в отдельный метод. Таким образом, вы можете подчеркнуть логику отбора и методов держать приятно маленький.

Правка: тока осущ:

for (Field field : type.getDeclaredFields()) {
if (isPersistentField(field)) {
field.setAccessible(true);
String fieldName = field.getName();
Object fieldValue = null;
try {
fieldValue = field.get(instance);
} catch (Exception e) {
throw new RuntimeException("foo", e);
}

if (!(fieldValue == null || fieldValue.equals(""))) {
fieldNameValuePairs.put(fieldName, fieldValue);
}
}
}

Вариант 1:

for (Field field : type.getDeclaredFields()) {
if (!isPersistentField(field)) {
continue;
}
field.setAccessible(true);
String fieldName = field.getName();
Object fieldValue = null;
try {
fieldValue = field.get(instance);
} catch (Exception e) {
throw new RuntimeException("foo", e);
}

if (!(fieldValue == null || fieldValue.equals(""))) {
fieldNameValuePairs.put(fieldName, fieldValue);
}
}

Альтернатива 2:

for (Field field : type.getDeclaredFields()) {
if (isPersistentField(field)) {
addNameValuePair(field, instance, fieldNameValuePairs);
}
}

...

private void addNameValuePair(Field field, Object instance, Map<String, Object> fieldNameValuePairs) {
Object fieldValue = valueOf(field, instance);
if (!(fieldValue == null || fieldValue.equals(""))) {
fieldNameValuePairs.put(field.getName(), fieldValue);
}
}

private Object valueOf(Field field, Object instance) {
boolean wasAccessible = field.isAccessible();
try {
field.setAccessible(true);
return field.get(instance);
} catch (Exception e) {
throw new RuntimeException("foo", e);
} finally {
field.setAccessible(wasAccessible);
}
}

6) настройка регистратора вместо системы.из -этак вы можете сохранить уровень отладочного журналов для последующего использования, если они необходимы.

7) СПД не требует, чтобы каждое поле Примеч. Это может вызвать некоторые проблемы в долгосрочной перспективе. Вы могли бы проверить, как объектом openjpa занимается этим. Они имеют свои собственные FieldMetaData реализация метода setManagement , который используется, чтобы сигнализировать, если поле является постоянным, транзакций или не удалось. Это значение в дальнейшем используется в ClassMetaData возвращает только те поля, которые управляются. Мне на самом деле стало скучно, прежде чем найти правильное место, где setManagement называется, но что он не должен быть слишком жестким в Google.

8) я предполагаю, что вы совершили умышленное решение не думать о возможности использовать аннотированный добытчики, а не поля.

Отказ от ответственности ни один из кодов не проверено

4
ответ дан 23 августа 2011 в 08:08 Источник Поделиться