Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Метод clone для List


Автор: gimp 6.2.2007, 15:23
Получаю какую-то информацию из БД (с помощью Hibernate):
Код

public List getUserList() {
  Session session = HibernateUtil.getSession();
  Query query = session.createQuery("from User");
  List result = query.list();

  return result;
}

И дальше мне нужно немного поработать с этим списком и его элементами, но не попортив информацию в БД. Обычно, для таких случаев есть метод clone(), который возвращает копию, которую можно смело менять, но в интерфейсе List почему-то нет метода clone(). Конечно же можно привести к ArrayList, у которого есть метод clone(), но ведь List, которые возвращает Hibernate, может и не привестись к ArrayList. Да и потом, насколько я понял из javadoc, метод clone() ArrayList'а элементы не копирует и все равно у меня все испортиться.
Как лучше поступать в подобных случаях?

P. S. я понимаю, что вопрос дурацкий, но просьба сильно не ругаться, я -- начинающий 

Автор: tux 6.2.2007, 17:33
Можно воспользоваться методом copy(java.util.List, java.util.List) класса http://java.sun.com/j2se/1.5.0/docs/api/java/util/Collections.html.

Автор: gimp 6.2.2007, 18:20
Цитата(tux @  6.2.2007,  17:33 Найти цитируемый пост)
Можно воспользоваться методом copy(java.util.List, java.util.List) класса java.util.Collections. 

Спасибо!

Автор: last 8.2.2007, 16:15
Это не решит проблемму до конца, тк. в получившемся листе будут копии ссылок на объекты, а не ссылки на копии объектов.

При попытке обратиться к свойствам любого бина в новом List вне сессии Hibernate получим исключение NullPointerException.

Вам придется написать для вашего List некий враппер, который переопределяет метод clone.
В этом методе нужно склонировать каждый объект в вашем листе и добавить в новый лист.

Естественно любой объект в вашем листе должен иметь метод clone и все поля этих объектов и тд.
Т.е. придеться писать глубокое клонирование.

Замечу, что метод Object.clone осуществляет только поверхностное копирование, те не копирует весь граф объектов, а ограничивается копированием ссылок.

Автор: _Y_ 9.2.2007, 11:18
Когда-то очень давно кто-то очень умный сказал мне: Только сам объект знает как себя клонировать. Поэтому: а) невозможно создать внешнюю процедуру корректно клонирушую произвольные объекты; б) Для каждого клонируемого объекта, где бы он ни содержался, надо прописывать метод clone.

Автор: Tony 9.2.2007, 12:00
Есть jakarta BeanUtils. Там есть два метода clone i copy bean. 
Код

И дальше мне нужно немного поработать с этим списком и его элементами, но не попортив информацию в БД.

?!?!?!? 
Пока ты не вызовешь save у хипера, данные не поменяются в ДБ. Может я 4тото не понял, то опиши.

Автор: Alexis 3.3.2007, 14:53
У меня та же проблема. Хотел создать тему в J2EE, все-таки там обычно обсуждают Hibernate, но раз уже есть такая тема, то напишу здесь.

Цитата(tux @  6.2.2007,  17:33 Найти цитируемый пост)
Можно воспользоваться методом copy(java.util.List, java.util.List) класса java.util.Collections. 

По причинам, которые описал last, не помогает. Правда к свойствам я обратиться могу, но вот изменение элементов списка сказываются и на БД. Может быть я не правильно пользуюсь Hibernate. Опишу подробнее.
Классы реализации DAO у меня построены примерно следующим образом:
Код

public class HibernateCategoryDao implements CategoryDao {
    public HibernateCategoryDao() {
        HibernateUtil.beginTransaction();
    }

    public List getCategoryList() {
        Session session = HibernateUtil.getSession();

        Query query = session.createQuery("from Category");
        List result = query.list();

        return result;
    }    

    public Category getCategoryById(Long id) {
        Session session = HibernateUtil.getSession();

        Category category = null;
        category = (Category)session.get(Category.class, id);

        return category;
    }

    public Long insertCategory(Category category) {
        Session session = HibernateUtil.getSession();

        session.save(category);

        return category.getId();
    }

    public boolean deleteCategoryById(Long id) {
        Session session = HibernateUtil.getSession();

        Category category = (Category) session.get(Category.class, id);

        try {
            session.delete(category);
        } catch (HibernateException e) {
            return false;
        }
        return true;
    }

    public boolean updateCategory(Category category) {
        Session session = HibernateUtil.getSession();

        try {
            session.update(category);
        } catch (HibernateException e) {
            return false;
        }

        return true;
    }

    // ...    
}

Класс HibernateUtil содран с Hibernate In Action
Код

public class HibernateUtil {    
    private static Logger logger = Logger.getLogger(HibernateUtil.class);
    private static final SessionFactory sessionFactory;
    private static final ThreadLocal threadSession = new ThreadLocal();
    private static final ThreadLocal threadTransaction = new ThreadLocal();
    
    static {
        try {            
            sessionFactory = new Configuration().configure()
                    .buildSessionFactory();
        } catch (Throwable ex) {            
            logger.error(ex.getMessage(), ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
    
    public static Session getSession() {
        Session s = (Session) threadSession.get();        
        try {
            if (s == null) {
                s = sessionFactory.openSession();
                threadSession.set(s);
            }
        } catch (HibernateException ex) {
            logger.error(ex.getMessage(), ex);
            // throw new InfrastructureException(ex);
        }

        return s;
    }
    
    public static void closeSession() {
        try {
            Session s = (Session) threadSession.get();
            threadSession.set(null);
            if (s != null && s.isOpen())
                s.close();
        } catch (HibernateException ex) {
            logger.error(ex.getMessage(), ex);
            // throw new InfrastructureException(ex);
        }
    }
    
    public static void beginTransaction() {
        Transaction tx = (Transaction) threadTransaction.get();
        try {
            if (tx == null) {
                tx = getSession().beginTransaction();
                threadTransaction.set(tx);
            }
        } catch (HibernateException ex) {
            logger.error(ex.getMessage(), ex);
            // throw new InfrastructureException(ex);
        }
    }
    
    public static void commitTransaction() {
        Transaction tx = (Transaction) threadTransaction.get();
        try {
            if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack())
                tx.commit();
            threadTransaction.set(null);
        } catch (HibernateException ex) {
            logger.error(ex.getMessage(), ex);
            // throw new InfrastructureException(ex);
        }
    }
    
    public static void rollbackTransaction() {
        Transaction tx = (Transaction) threadTransaction.get();
        try {
            threadTransaction.set(null);
            if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack())
                tx.rollback();
        } catch (HibernateException ex) {
            logger.error(ex.getMessage(), ex);
            // throw new InfrastructureException(ex);
        } finally {
            closeSession();
        }
    }

}

Commit делается в фильтре (у меня WEB-приложение):
Код

public class HibernateFilter implements Filter {

    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {
        try {
            chain.doFilter(req, resp);
            HibernateUtil.commitTransaction();
        } finally {
            HibernateUtil.closeSession();
        }
    }

    public void init(FilterConfig arg0) throws ServletException {
    }

}

Код

<filter-mapping>
        <filter-name>HibernateFilter</filter-name>
        <url-pattern>/*</url-pattern>
</filter-mapping>


В общем-то в чем состоит проблема. Получаю я, к примеру, список категорий и временно (т. е. их имена в БД менять мне не нужно) меняю их имена:
Код

List categories = categoryDao.getCategoryList();
Iterator it = categories.iterator();
while (it.hasNext()) {
        Category сategory = (Category) it.next();

        category.setName(getFullName(сategory, separator));
        fullname.add(category);
}

В результате имена меняются и БД.

Цитата(last @  8.2.2007,  16:15 Найти цитируемый пост)
Вам придется написать для вашего List некий враппер, который переопределяет метод clone.
В этом методе нужно склонировать каждый объект в вашем листе и добавить в новый лист.

В-принципе, мне подходит этот вариант, так сейчас и сделано. Но как-то хлопотно получается, да и что-то мне подсказывает, что как-то неправильно я hibernate использую. Хотелось бы узнать как уважаемые участники поступают сами в таких ситуациях, общепринятый подход так сказать.

Автор: LSD 3.3.2007, 15:19
Что храниться в List?
Метод clone() у Object имеет модификатор protected. Так что мы должны будем приводить объекты к некому типу у которого метод clone() имеет модификатор public.

Добавлено @ 15:20 
В принципе есть методика глубокого клонирования, через сериализацию.

Автор: Alexis 3.3.2007, 15:27
Цитата(LSD @  3.3.2007,  15:19 Найти цитируемый пост)
Что храниться в List?

Объект типа Category
Клонировать эти объекты не проблема, но может быть есть способ как-то "отцепить" этот список, чтобы Hibernate не следил за его изменением? Или в таких случаях обычно делают клонирование?

Автор: LSD 4.3.2007, 12:19
Клонируй список.

Автор: Alexis 4.3.2007, 13:02
Цитата(LSD @  4.3.2007,  12:19 Найти цитируемый пост)
Клонируй список. 

Спасибо за ответ. Тогда так и оставлю клонирование.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)