1

I am doing it following way. It's making me feel sick!

public class CounselInfoServiceImpl
    extends BaseServiceImpl<CounselInfoDao, CounselInfoEntity, Long>
    implements CounselInfoService {

    @Inject
    ClassService classService;

    @Inject
    @Override
    public void setDao(CounselInfoDao dao)
    {
        super.setDao(dao);
    }

    @Override
    public CounselInfoEntity editTo(CounselInfoEntity model)
    {
        CounselInfoEntity entity = id(model.getId());

        if (!Strings.isNullOrEmpty(model.getName()))
        {
            entity.setName(model.getName());
        }

        if (!Strings.isNullOrEmpty(model.getAddress()))
        {
            entity.setAddress(model.getAddress());
        }

        if (!Strings.isNullOrEmpty(model.getEducation()))
        {
            entity.setEducation(model.getEducation());
        }

        if (!Strings.isNullOrEmpty(model.getPhone()))
        {
            entity.setPhone(model.getPhone());
        }

        if (!Strings.isNullOrEmpty(model.getQQ()))
        {
            entity.setQQ(model.getQQ());
        }

        if (!Strings.isNullOrEmpty(model.getRemark()))
        {
            entity.setPhone(model.getPhone());
        }

        if (!Strings.isNullOrEmpty(model.getSchool()))
        {
            entity.setSchool(model.getSchool());
        }

        if (model.getAge() != null)
        {
            entity.setAge(model.getAge());
        }

        if (model.getSex() != null)
        {
            entity.setSex(model.getSex());
        }

        if (model.getClassIntention() != null)
        {                           
            entity.setClassIntention(
                      classService.id(
                            model.getClassIntention().getId()));
        }

        return entity;
    }
}

Any suggestions to avoid this spaghetti code ?

BTW, writing this code is a hard work!

EDIT

BTW, I don't think the em.merge is ready for this. See here

The EntityManager.merge() operation is used to merge the changes made to a detached object into the persistence context.

It mentioned the detached object, but the update model just got a piece of date. So, if I merge the model, all of model's value will apply to entity.(e.g. password, which I don't want to update, and the editTo should not touch the password.)

wener
  • 7,191
  • 6
  • 54
  • 78
  • What you have there is a strange way of doing. Usually, when using an app to edit an entity, the user uses a pre-filled form containing all the fields of the entity, modifies the fields that must be modified, submits this form, and all the fields, empty or not, are written to the entity. I've never seen a form where leaving a field empty means "don't modify the value of this attribute". – JB Nizet Apr 27 '14 at 07:24
  • No empty field in form, because I will not display those field.Some filed(s) is verify sensitive.So, the user can edit just part of the whole date. – wener Apr 27 '14 at 08:06
  • I don't understand what that means. Which date are you talking about? What does the form look like? What does "verify sensitive" mean? – JB Nizet Apr 27 '14 at 08:09
  • The form will not display all date, so, the model will not get all date.When update, I have to test which part is modified.But, sometime, even the date in model is changed, I should not make the change to entity(e.g. the `version` field).And I find some other way, I will post after I clear my thought. – wener Apr 27 '14 at 10:58

1 Answers1

0

Now, the update looks like this.

public CounselInfoEntity editTo(CounselInfoEntity model)
{
    CounselInfoEntity entity = id(model.getId());

    List<? extends Attribute<CounselInfoEntity, ?>> editAttrs = Lists.<Attribute<CounselInfoEntity, ?>>newArrayList(CounselInfoEntity_.name,
    CounselInfoEntity_.address,
    CounselInfoEntity_.education,
    CounselInfoEntity_.phone,
    CounselInfoEntity_.QQ,
    CounselInfoEntity_.remark,
    CounselInfoEntity_.school,
    CounselInfoEntity_.age,
    CounselInfoEntity_.sex);

    BeanHelper.merge(entity, model, BeanHelper.skipNullOrEmpty(model, editAttrs));

    if (model.getClassIntention() != null)
    {                           
        entity.setClassIntention(classService.id(model.getClassIntention().getId()));
    }

    return entity;
}

Here is BeanHelper

package me.wener.practices.web.common.util;

import com.google.common.collect.Lists;
import java.lang.reflect.Field;
import java.util.List;
import javax.persistence.metamodel.Attribute;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.reflect.FieldUtils;

@Slf4j
public class BeanHelper
{
    /**
     * 获取 bean 的属性,如果属性不存在或发生异常返回null
     */
    public static Object tryGetProperty(Object bean, String attrName)
    {
        Object property = null;
        try
        {
            Field field = FieldUtils.getField(bean.getClass(), attrName, true);
            property = field.get(bean);
        } catch (Exception e)
        {
            if (log.isErrorEnabled())
                log.error("Exception when get property " + attrName + " on " + bean, e);
        }
        return property;
    }

    public static <T, A extends Attribute<T, ?>> Object tryGetProperty(T bean, A attr)
    {
        return tryGetProperty(bean, attr.getName());
    }

    public static <T, A extends Attribute<T, ?>> boolean trySetProperty(T bean, A attr, Object value)
    {
        return trySetProperty(bean, attr.getName(), value);
    }

    public static boolean trySetProperty(Object bean, String attrName, Object value)
    {
        boolean failed = false;
        try
        {
            // 对于 chain 的 setter 方法, 必须要使用 force access.
            Field field = FieldUtils.getField(bean.getClass(), attrName, true);
            field.set(bean, value);
        } catch (Exception e)
        {
            if (log.isErrorEnabled())
                log.error("Exception when set property " + attrName + " on " + bean, e);

            failed = true;
        }
        return !failed;
    }

    /**
     * Test the value of search in attrs is make the isNull and isEmpty
     * <p/>
     * isEmpty will apply when value is String
     */
    @SafeVarargs
    public static <E, A extends Attribute<E, ?>> List<A> skip(Object searcher, boolean skipNull, boolean skipEmpty, A... attrs)
    {
        return skip(searcher, skipNull, skipEmpty, Lists.newArrayList(attrs));
    }

    public static <E, A extends Attribute<E, ?>> List<A> skip(Object searcher, boolean skipNull, boolean skipEmpty, List<A> attrs)
    {
        List<A> list = Lists.newArrayList();
        boolean valid;

        for (A attr : attrs)
        {
            Object value = tryGetProperty(searcher, attr.getName());
            valid = skipNull || value != null;

            if (valid && skipEmpty && value instanceof String)
                valid = ((String) value).length() != 0;

            if (valid)
                list.add(attr);
        }
        return list;
    }

    @SafeVarargs
    public static <E, A extends Attribute<E, ?>> List<A> skipNullOrEmpty(Object searcher, A... attrs)
    {
        return skip(searcher, true, true, attrs);
    }

    public static <E, A extends Attribute<E, ?>> List<A> skipNullOrEmpty(Object searcher, List<A> attrs)
    {
        return skip(searcher, true, true, attrs);
    }

    @SafeVarargs
    public static <T, A extends Attribute<T, ?>> T merge(T target, T src, A... attrs)
    {
        return merge(target, src, Lists.newArrayList(attrs));
    }

    public static <T, A extends Attribute<T, ?>> T merge(T target, T src, List<A> attrs)
    {
        for (A attr : attrs)
        {
            String attrName = attr.getName();
            Object value = tryGetProperty(src, attrName);
            trySetProperty(target, attrName, value);
        }

        return target;
    }
}

This one is better, because

  • It's type safe
  • Easy to filter/choose property to merge

It include

I try my best, this is the best I can do.

Community
  • 1
  • 1
wener
  • 7,191
  • 6
  • 54
  • 78