Поскольку в процессе продолжения обучения решил разобраться с Hibernate для организации работы с базами данных посредством максимального ухода от использования SQL-запросов, то было решено сформировать некий абстрактный пример. Он должен быть с одной стороны простым, а с другой дать возможность проверить работоспособность всех типовых операций, которые при обращении к БД возникают.

Ниже представляю на суд свое творение. Изначально я опирался на следующий материал. Как по мне он довольно интересен, но всегда интересно сформировать полное приложение, пусть и простое. Итак, что же получилось…

Исходная база осталась неизменной, как в указанной выше методике. image

Перечень операций, которые необходимо будет реализовать, будет выглядеть так:
  • Добавить новый отдел;
  • Править данные отдела;
  • Удалить отдел;
  • Вывести данные отделов;
  • Найти отдел по коду;
  • Добавить нового сотрудника;
  • Просмотр сотрудников по отделу;
  • Удалить сотрудника.


Первый шаг — было формирование маппингов к базе. Из того материала, что рассмотрел сам, понял что есть два пути: использования xml — файла или использование аннотаций. Для упрощения восприятия, отдельный xml- файл для моего примера для отдела сотрудников выглядит так:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>

    <class name="logic.Department" table="department" schema="joblist">
        <id name="departmentId">
            <column name="department_id" sql-type="int(11)"/>
            <generator class="increment"/>
        </id>
        <property name="caption">
            <column name="caption" sql-type="text" not-null="true"/>
        </property>

        <set name="employies" lazy="false" cascade="all" inverse="true">
            <key column="fk_department_id"/>
            <one-to-many class="logic.Employee"/>
        </set>
    </class>
</hibernate-mapping>


Сразу первая оговорка, в файле должен присутствовать заголовок… <?xml version='1.0' encoding='utf-8'?> и т.д.

Пытался реализовать это же самое через аннотации, которые выглядят так:
@Entity
@Table(name="department")
public class Departmant {

    private long id;
    private String name;

    public Department() {
    }


Строки, начинающиеся с @ — и есть аннотации. Что именно использовать, решать авторам самостоятельно. Обе версии имеют право на жизнь.

Второй маппинг для сотрудников выглядит так:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>

    <class name="logic.Employee" table="employee" schema="joblist">
        <id name="employeeId">
            <column name="employee_id" sql-type="int(11)"/>
            <generator class="increment"/>
        </id>
        <property name="fio">
            <column name="fio" sql-type="text" not-null="true"/>
        </property>

        <many-to-one name="department" class="logic.Department" column="fk_department_id" not-null="true" cascade="save-update"/>
    </class>
</hibernate-mapping>


Сразу внесу замечания по формированию связей. Поскольку используется связь 1-М (один ко многим), то со стороны базовой сущности (отдел) указываем:
<set name="employies" lazy="false" cascade="all" inverse="true">

Ленивое соединение с подчиненной таблицей убрано (возможно это не совсем правильно, еще буду смотреть), используется каскадное взаимодействие между главной и подчиненной таблицами, и инверсия — включена, то есть связь между таблицами в обеих направлениях, по сути — одна связь (как оно и есть с СУБД).

Со стороны подчиненной сущности укажем:
 <many-to-one name="department" class="logic.Department" column="fk_department_id" not-null="true"
                     cascade="save-update"/>

То есть, ключевое поле (внешний ключ) не может быть нулевым (обеспечение целостности на уровне БД) и выполнение каскадных операций допустимо для сохранения или обновления (иначе при удалении сотрудника слетит и его родной отдел).

Классы «Отдел» и «Сотрудники» — это типичные POJO, как показано во многих примерах.
package logic;

import java.util.HashSet;
import java.util.Set;

public class Department {
    private int departmentId;
    private String caption;


    public Department() {
    }

    public Department(String caption) {
        this.caption = caption;
    }

    public int getDepartmentId() {
        return departmentId;
    }

    public void setDepartmentId(int departmentId) {
        this.departmentId = departmentId;
    }

    public String getCaption() {
        return caption;
    }

    public void setCaption(String caption) {
        this.caption = caption;
    }



    Set<Employee> employies = new HashSet<Employee>();

    public Set<Employee> getEmployies(){
        return employies;
    }

    public void setEmployies(Set<Employee> employies) {
        this.employies = employies;
    }

    @Override
    public String toString() {
        String string = this.departmentId + " " + this.caption;
        return string;
    }
}


package logic;

public class Employee {
    private int employeeId;
    private String fio;
    private Department department;

    public Employee() {
    }

    public Employee(String fio) {
        this.fio = fio;
    }


    public int getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(int employeeId) {
        this.employeeId = employeeId;
    }


    public String getFio() {
        return fio;
    }

    public void setFio(String fio) {
        this.fio = fio;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department){
        this.department = department;
    }

    @Override
    public String toString() {
        String string = this.employeeId+ " " + this.fio;
        return string;
    }
}


Продолжаем далее… Необходимо сформировать класс — фабрику, которая будет выполнять однократный запуск конфигурации и списка методов обработки (интерфейсов). Это реализовано в виде синглетонов.

package factory;

import DAO.DepartmentDAO;
import DAO.EmployeeDAO;
import implement.ImplDepartment;
import implement.ImplEmployee;

public class Factory {

    private static DepartmentDAO departmentDAO = null;
    private static EmployeeDAO employeeDAO = null;
    private static Factory instance = null;

    public static synchronized Factory getInstance() {
        if (instance == null) {
            instance = new Factory();
        }
        return instance;
    }

    public DepartmentDAO getDepartmentDAO() {
        if (departmentDAO == null) {
            departmentDAO = new ImplDepartment();
        }
        return departmentDAO;
    }

    public EmployeeDAO getEmployeeDAO() {
        if (employeeDAO == null) {
            employeeDAO = new ImplEmployee();
        }
        return employeeDAO;
    }
}


Конфигурирования hibernate — файл hibernate.cfg.xml имеет структуру:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
    <property name="connection.url">jdbc:mysql://localhost:3306/Joblist</property>
    <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="connection.username">root</property>
    <property name="connection.password">admin</property>
    <property name="connection.pool_size">1</property>
    <property name="show_sql">true</property>
    <property name="current_session_context_class">thread</property>
    <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>

    <mapping resource="logic/Employee.hbm.xml"/>
    <mapping resource="logic/Department.hbm.xml"/>
    <mapping class="logic.Department"/>
    <mapping class="logic.Employee"/>


  </session-factory>
</hibernate-configuration>


Следующий шаг — создание класса для формирования сессии работы. Обычный вариант, рассматриваемый во множестве примеров, хотя и работает, но имеет статус «Deprecated» — не рекомендовано к применению. В результате, утилита для организации соединения имеет несколько иной вид:

package utils;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;


public class HibernateUtil {

    private static SessionFactory sessionFactory;
    private static ServiceRegistry serviceRegistry;

    public static final SessionFactory createSessionFactory() {
        Configuration configuration = new Configuration();
        configuration.configure();
        serviceRegistry = new ServiceRegistryBuilder().applySettings(
                configuration.getProperties()).buildServiceRegistry();
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        return sessionFactory;
    }
}


Классы — интерфейсы для организации операций с таблицами, выглядят так:
package DAO;

import logic.Department;
import logic.Employee;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Collection;

public interface DepartmentDAO {

    public void addDepartment(Department department) throws SQLException;

    public void editDepartment(Department department) throws SQLException;

    public void deleteDepartment(Integer department) throws SQLException, IOException;

    public Collection getDepartmentById(Integer department_id) throws SQLException;

    public Collection getAllDepartments() throws SQLException;

    public void addEmployee(Department department, Employee employee) throws SQLException;

}


Во втором случае, оговорюсь сразу, методы реализовал не все…

package DAO;

import logic.Employee;

import java.sql.SQLException;
import java.util.Collection;

public interface EmployeeDAO {

    public void editEmployee (Employee employee) throws SQLException;

    public void deleteEmployee (Employee employee) throws SQLException;

    public Collection getDepartmentByEmployee (Employee employee) throws SQLException;

    public Collection getAllEmployies() throws SQLException;

    public void deleteEmployiesByName(String name) throws SQLException;

    public Collection getEmployiesByDepartment(Integer department) throws SQLException;
}


Далее, раз есть методы, то их необходимо реализовать:

package implement;


import DAO.DepartmentDAO;
import logic.Department;
import logic.Employee;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import utils.HibernateUtil;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.util.*;

public class ImplDepartment implements DepartmentDAO {
    @Override
    public void addDepartment(Department department) throws SQLException {
        Session session = null;
        try {
            session = HibernateUtil.createSessionFactory().openSession();
            session.beginTransaction();
            session.save(department);
            session.getTransaction().commit();
        } catch (HibernateException exception) {
            System.out.println("Ошибка добавления отдела");
            exception.printStackTrace();
        } finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
    }

    @Override
    public void editDepartment(Department department) throws SQLException {
        Session session = null;
        try {
            session = HibernateUtil.createSessionFactory().openSession();
            session.beginTransaction();
            session.update(department);
            session.getTransaction().commit();
        } catch (HibernateException exception) {
            System.out.println("Ошибка правки отдела");
            exception.printStackTrace();
        } finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
    }

    @Override
    public void deleteDepartment(Integer id) throws SQLException, IOException {
        Session session = null;
        try {
            session = HibernateUtil.createSessionFactory().openSession();
            session.beginTransaction();
            Department department = (Department) session.load(Department.class, id);
            Employee[] employies = department.getEmployies().toArray(new Employee[]{});
            if (employies.length > 0) {
                System.out.println("В отделе есть сотрудники, удалить?");
                BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                String ch = reader.readLine();
                if (ch.equals("Y") || (ch.equals("y"))) {
                    session.delete(department);
                    session.getTransaction().commit();
                }
            } else {
                session.delete(department);
                session.getTransaction().commit();
            }
        }
         catch (HibernateException exception) {
            System.out.println("Ошибка удаления отдела");
            exception.printStackTrace();
        } finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
    }

    @Override
    public Collection getDepartmentById(Integer department_id) throws SQLException {
        Session session = null;
        List departments = new ArrayList<Department>();
        try {

            session = HibernateUtil.createSessionFactory().openSession();
            departments = session.createCriteria(Department.class).add(Restrictions.idEq(department_id)).list();
        } catch (HibernateException exception) {
            System.out.println("Ошибка отбора отдела");
            exception.printStackTrace();
        } finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
        return departments;
    }

    @Override
    public Collection getAllDepartments() throws SQLException {
        Session session = null;
        List departments = new ArrayList<Department>();
        try {
            session = HibernateUtil.createSessionFactory().openSession();
            departments = session.createCriteria(Department.class).list();
        } catch (Exception e) {
            System.out.println("Ошибка чтения списка отделов");
        } finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
        return departments;
    }

    @Override
    public void addEmployee(Department department, Employee employee) throws SQLException {
        Session session = null;
        try {
            session = HibernateUtil.createSessionFactory().openSession();
            session.beginTransaction();
            department.getEmployies().add(employee);
            employee.setDepartment(department);
            session.saveOrUpdate(department);
            session.saveOrUpdate(employee);
            session.getTransaction().commit();
        } catch (HibernateException exception) {
            exception.printStackTrace();
            System.out.println("Ошибка добавления сотрудника");
        } finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
    }
}



package implement;


import DAO.EmployeeDAO;
import logic.Department;
import logic.Employee;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import utils.HibernateUtil;
import java.sql.SQLException;
import java.util.*;

public class ImplEmployee implements EmployeeDAO {


    @Override
    public void editEmployee(Employee employee) throws SQLException {
        Session session = null;
        try {
            session = HibernateUtil.createSessionFactory().openSession();
            session.beginTransaction();
            session.update(employee);
            session.getTransaction().commit();
        } catch (HibernateException exception) {
            System.out.println("Ошибка правки сотрудника");
            exception.printStackTrace();
        } finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
    }

    @Override
    public void deleteEmployee(Employee employee) throws SQLException {

    }

    @Override
    public Collection getDepartmentByEmployee(Employee employee) throws SQLException {
        return null;
    }

    @Override
    public Collection getAllEmployies() throws SQLException {
        return null;
    }


    @Override
    public void deleteEmployiesByName(String name) throws SQLException {
        Session session = null;
        List employies = new ArrayList<Employee>();
        try {
            session = HibernateUtil.createSessionFactory().openSession();
            session.beginTransaction();
            employies = session.createCriteria(Employee.class).add(Restrictions.like("fio", name)).list();
            Iterator iterator = employies.iterator();
            while (iterator.hasNext()) {
                Employee data = (Employee) iterator.next();
                String nameTemp = data.getFio();
                if (nameTemp.equals(name)){
                    int id = data.getEmployeeId();
                    Object person =  session.load(Employee.class, id);
                    session.delete(person);
                }
            }
            session.getTransaction().commit();
        } catch (HibernateException exception) {
            System.out.println("Ошибка удаления сотрудника");
            exception.printStackTrace();
        } finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
    }

    @Override
    public Collection getEmployiesByDepartment(Integer department) throws SQLException {
        Session session = null;
        List departments = new ArrayList<Department>();
        Collection employies = null;
        try {
            session = HibernateUtil.createSessionFactory().openSession();
            departments = session.createCriteria(Department.class).add(Restrictions.idEq(department)).list();
            Iterator iterator = departments.iterator();
            while (iterator.hasNext()) {
                Department data = (Department) iterator.next();
                employies = data.getEmployies();
            }
        } catch (Exception e) {
            System.out.println("Ошибка чтения списка сотрудников");
        } finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
        return employies;
    }
}


Пустышки с результатом Null и есть пока не реализованные методы. Но они не испрашиваются из основного класса, поэтому проблем с этим нет, доделать можно в любое время. Да и в принципе, состав методов может меняться по обстановке.

Ну и наконец — основной модуль, чтобы проверить всю правильность реализованных идей.
package main;

import factory.Factory;
import logic.Department;
import logic.Employee;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Main {

    public static void addDepartment() throws IOException, SQLException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("Введите наименование отдела: ");
        String data = reader.readLine();
        Department department = new Department();
        department.setCaption(data);
        Factory.getInstance().getDepartmentDAO().addDepartment(department);
    }

    public static void editDepartment() throws IOException, SQLException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("Введите шифр отдела: ");
        int num = 0;
        String data = reader.readLine();
        try {
            num = Integer.parseInt(data);
            Collection collection;
            collection = Factory.getInstance().getDepartmentDAO().getDepartmentById(num);
            Set<Department> set = new HashSet<>(collection);
            Iterator iterator = set.iterator();
            while (iterator.hasNext()) {
                Department department = (Department) iterator.next();
                Integer id = department.getDepartmentId();
                String name;
                System.out.println("Введите новое название");
                name = reader.readLine();
                Department temporal = new Department();
                temporal.setDepartmentId(id);
                temporal.setCaption(name);
                Factory.getInstance().getDepartmentDAO().editDepartment(temporal);
            }
        } catch (NumberFormatException e) {
            System.out.println("Введено не число");
        }
    }

    public static void getDepartments() throws SQLException {
        Collection collection;
        collection = Factory.getInstance().getDepartmentDAO().getAllDepartments();
        showDepartments(collection);
    }

    public static void findDepartments() throws SQLException, IOException {
        System.out.println("Введите код отдела");
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String data = reader.readLine();
        try {
            int num = Integer.parseInt(data);
            Collection collection;
            collection = Factory.getInstance().getDepartmentDAO().getDepartmentById(num);
            showDepartments(collection);
        } catch (NumberFormatException e) {
            System.out.println("Введено не число");
        }
    }

    public static void findEmployies() throws SQLException, IOException {
        System.out.println("Введите код отдела для выборки");
        int num = 0;
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        try {
            String data = reader.readLine();
            num = Integer.parseInt(data);
            Collection collection = Factory.getInstance().getDepartmentDAO().getDepartmentById(num);
            showDepartments(collection);
            if (collection.size() != 0) {
                Collection employies = Factory.getInstance().getEmployeeDAO().getEmployiesByDepartment(num);
                showEmployers(employies);
            } else System.out.println("Такого отдела нет");
        } catch (NumberFormatException e) {
            System.out.println("Введено не число");
        }
    }


    public static void showDepartments(Collection collection) throws SQLException {
        Set<Department> set = new HashSet<Department>(collection);
        Iterator iterator = set.iterator();
        System.out.println("====================================");
        System.out.printf("%-20s%-20s%n", "Шифр отдела", "Название отдела");
        System.out.println("====================================");
        while (iterator.hasNext()) {
            Department data = (Department) iterator.next();
            System.out.printf("%-20s%-20s%n", data.getDepartmentId(), data.getCaption());
        }
    }

    public static void showEmployers(Collection collection) throws SQLException {
        Set<Employee> set = new HashSet<Employee>(collection);
        Iterator iterator = set.iterator();
        System.out.println("====================================");
        System.out.printf("%-20s%n", "ФИО сотрудника отдела");
        System.out.println("====================================");
        while (iterator.hasNext()) {
            Employee data = (Employee) iterator.next();
            System.out.printf("%-20s%n", data.getFio());
        }
    }

    public static void deleteDepartment() throws SQLException, IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int num = 0;
        System.out.println("Введите код отдела");
        try {
            String data = reader.readLine();
            num = Integer.parseInt(data);
            Factory.getInstance().getDepartmentDAO().deleteDepartment(num);
        } catch (NumberFormatException e) {
            System.out.println("Введено не число");
        }
    }


    public static void deleteEmployies() throws SQLException, IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("Введите ФИО сотрудника");
        String name = reader.readLine();
        Collection collection;
        Factory.getInstance().getEmployeeDAO().deleteEmployiesByName(name);
    }


    public static void addEmployee() throws SQLException, IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("Введите шифр отдела");
        String id = reader.readLine();
        try {
            int num = Integer.parseInt(id);
            Collection collection;
            collection = Factory.getInstance().getDepartmentDAO().getDepartmentById(num);
            showDepartments(collection);
            Set<Department> set = new HashSet<>(collection);
            if (set.size() != 0) {
                System.out.println("Введите ФИО сотрудника");
                String data = reader.readLine();
                Employee employee = new Employee(data);
                Iterator iterator = set.iterator();
                while (iterator.hasNext()) {
                    Department department = (Department) iterator.next();
                    Factory.getInstance().getDepartmentDAO().addEmployee(department, employee);
                }
            } else System.out.println("Такого отдела нет");
        } catch (NumberFormatException e) {
            System.out.println("Введено не число");
        }

    }


    static public void main(String[] args) throws IOException, SQLException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (true) {
            System.out.println("====================================");
            System.out.println("Выберите режим работы с базой");
            System.out.println("====================================");
            System.out.println("1 - Добавить новый отдел");
            System.out.println("2 - Править данные отдела");
            System.out.println("3 - Удалить отдел");
            System.out.println("4 - Вывести данные отделов");
            System.out.println("5 - Найти отдел по коду");
            System.out.println("====================================");
            System.out.println("6 - Добавить нового сотрудника");
            System.out.println("7 - Просмотр сотрудников по отделу");
            System.out.println("8 - Удалить сотрудника");
            System.out.println("====================================");
            System.out.println("0 - Завершить работу");
            System.out.println("====================================");
            String num = reader.readLine();
            switch (num) {
                case "1":
                    addDepartment();
                    break;
                case "2":
                    editDepartment();
                    break;
                case "3":
                    deleteDepartment();
                    break;
                case "4":
                    getDepartments();
                    break;
                case "5":
                    findDepartments();
                    break;
                case "6":
                    addEmployee();
                    break;
                case "7":
                    findEmployies();
                    break;
                case "8":
                    deleteEmployies();
                    break;
                case "0":
                    System.exit(0);
                    break;
            }
        }
    }
}


Итог: на что нужно обратить внимание. Во первых, на правильность структуры файлов для маппинга и организацию связей. Результаты ошибок могут заставить понервничать. Во вторых, на новую организацию сессии. В третьих, иногда в методиках предлагают формировать POJO в авторежиме, в этом случае придется установить соединение с базой данных еще в ходе проектирования. Результат будет в виде аннотаций (описание компонентов БД внутри классов).

Надеюсь, что пример хотя и простой, но пригодится для получения первых впечатлений.
Поделиться с друзьями
-->

Комментарии (61)


  1. mixailflash
    11.07.2016 15:57
    -1

    Хороший пост, особенно для тех кто хочет разобраться с нуля. Побольше бы таких мануалов.


    1. Stalker_27
      11.07.2016 15:59

      Не поверишь, сам часто ищу такие.


      1. mixailflash
        11.07.2016 16:02

        Поверю. Очень много статей тика «Hello World», а потом сразу же «настраиваем свой кластер из первой сотни машин». А промежуточных статей очень мало.


        1. Stalker_27
          11.07.2016 16:19

          Что есть — то есть.


        1. solver
          12.07.2016 13:01

          Вот уж почему, а по хиберу материала полно, особенно вот такого «начального».
          И совсем необязательно брать материалы 8 летней давности для обучения.


          1. mixailflash
            12.07.2016 13:09

            Возможно и не так мало как я написал, но ничего плохого в еще одном посте я не вижу.


  1. solver
    11.07.2016 16:39
    +1

    А почему xml в 2016 году?


    1. Stalker_27
      11.07.2016 16:42
      -2

      то есть — xml для маппинга?


      1. solver
        11.07.2016 17:41

        Ну да. На аннтоациях удобнее и нагляднее.


        1. alexei-grigoriev
          12.07.2016 11:09

          Ну, например, аннотации из POJO делают не-POJO.


  1. olegchir
    11.07.2016 17:07
    +3

    Простите меня пожалуйста, потому что сейчас будет критика без приведения конструктива.
    Потому что на конструктив нужно очень много времени и кода, а если я буду посреди рабочего дня катать простыни на хабр — меня уволят :(

    Так вот. Здесь написан лютый ад. Который приведет к куче проблем. Мы тут поревьюили этот туториал, и никак не оправимся от шока, кровь стынет в жилах.
    В 2016 году всё это делается немного по-другому, и поэтому, цитирую, «Результаты ошибок могут заставить понервничать. » — почти не происходит.
    Такие дела.

    Возможно, стоит написать книжку по Hibernate на русском языке, где такие вопросы будут подробно рассмотрены?


  1. Stalker_27
    11.07.2016 17:15

    Да тут будет много желающих, если книжка такового уровня появится… То есть, Вы хотите ее написать, если я правильно понял?


  1. Stalker_27
    11.07.2016 17:21

    Просто есть некоторое впечатление (ну может я и не прав), что расклад такой…
    1. Кто знает как нужно, и знает как нужно именно правильно (позитивный опыт) — ему и так хорошо.
    2. Кто хочет узнать, пользуется (и в принципе и должен начинать пользоваться) тем, что наработано до него.
    3. Естественно, какой материал (подход) изложен, на том и «летают»…

    Читать мануалы — это отлично, и правильно… но кто знает водителя, который научился ездить, читая руководство по эксплуатации автомобиля. Если вы хотите поделиться опытом, все ждем… ) Подпишусь на ваши статьи первым.


    1. olegchir
      11.07.2016 17:34
      +1

      обратите внимание, что вы отвечаете не на мой комментарий, а в пустоту.

      по вопросу передачи знаний — обычно оно передается от учителя к ученику в джава-компаниях. Код, в котором сто сорок раз скопипастана кривая обертка запуска функции в транзакции, просто не пройдет первое же ревью. Там достаточно всего несколько дней, чтобы набить шишки, поэтому такие вещи становятся самоочевидными и ни в какие мануалы не попадают


    1. olegchir
      11.07.2016 18:17
      +5

      короче, вот, закончил таску, давайте по-бырому что нужно сделать:

      1) выкосить все маппинги в XML, так никто не делает.
      (без веских оснований, а у совсем новичка этих веских оснований быть не может)
      да, придется попотеть. Да, нужно.

      2) заметка для обучения: чтобы не читать весь талмуд документации, можно прочитать всего одну документацию: https://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/
      Каждый пункт из этой документации нужно попробовать хотя бы по 1 разу.

      3) никогда не копипастайте DAO
      вот первое что нашлось в гугле (эта статья написана, когда я был еще маленьким: https://www.ibm.com/developerworks/ru/library/j-genericdao/)

      4) и вообще ничего не копипастайте. Особенно вот это открытие сессии сто раз.

      3) следующим вашим желанием будет написать свое велосипедное DAO. Если вы фигачите в продакшен, от этого желания нужно избавиться и взять что-нибудь готовое. Например, Hibernate Generic Dao (у него кажется загнулся сайт, так что придется вам самим нагуглить документацию. В Maven Central он лежит, а исходник вот: https://github.com/based2/hibernate-generic-dao)

      5) сейчас все пользуются Maven'ом, поэтому неплохо было бы увидеть помку для вашего проекта. Со всеми зависимостями.

      6) запускать Hibernate в main функции — это весьма странно даже для туториала. Там есть ряд проблем, связанных с тем, что разрабы фреймворков рассчитывают, что вы будете запускать приложение в контейнере или апликейшен-сервере.

      Вместо main(), возьмите какой-нибудь Tomcat или Wildfly.

      7) так как у вас уже появился контейнер, сразу же добавьте туда Spring и выкосите ручную работу с транзакциями. Вам нужна аннотация @Transactional.

      8) а если уж так прикипело душой к интерфейсу командной строки, то хотя бы уберите этот жуткий свич и впилите Apache Commons CLI. Тогда можно будет легко модифицировать ваш пример для того, чтобы методы у энтити принимали параметры, а не просто 12345. Код консольного гуя тоже нужно причесать и отрефакторить: например, ввод строк пользователя лучше вынести в отдельные методы, а не фигачить readline прямо посреди бизнес-логики, а то получилась каша

      9) сообщения лога никогда не выводятся через println или упаси б-же printStackTrace. Используйте логгер из slf4j.

      10) Set, Map и прочие вещи, котоыре основываются на hashcode — это очень плохо для Hibernate. Вообще в джаве с equals всё очень плохо. По возможности, не пользуйтесь этим. Если можно без хлопот использовать листы — используйте листы.

      11) Там по тексту где-то были итераторы — по возможности, не пользуйтесь ими, нужно использовать упрощенную запись for, либо Stream API

      12) Держите в уме стандартную архитектуру: DAO отдельно, сервисы — отдельно. Подробней пришлось бы написать поэму, в сети есть что-то про это, погуглите какие хорошие практики для написания дао и сервисов, и как их использовать хотя бы внутри MVC: http://programmers.stackexchange.com/questions/220909/service-layer-vs-dao-why-both

      13) Блокирующие операции типа readline должны быть отдельно от пакетизированной логики. То есть вы сначала собираете все данные для операции, и только потом уже эту пачку данных отправляете на обработку в сервис. Читать строчку и тут же делать из нее класс — это фу.

      14) Для всего что нужно закрывать — используйте try with resources.
      Вообще насчет траев — провентилируйте как это сделано во фреймворках, все ли вы ошибочные ситуации рассмотрели. (Spring HibernateTemplate?)


      1. asm0dey
        11.07.2016 19:30

        Листы в хибере нельзя — при изменении состава коллекции происходит сначала удаление всего, а потом куча инсертов. equals и hashCode надо переопределить на основе айдишника.


        1. olegchir
          12.07.2016 16:34

          @OrderColumn должно спасти отца русского маппинга


        1. olegchir
          12.07.2016 16:45

          и даже если втащить под ключ id, — это не выйдет потому, что если объект лежит еще и в какой-то мапе, а мы ему при сохранении сгенерим хэшкод — то объект просто в этой мапе потеряется

          да и вообще, если у нас id сгенерирован, то уже по смыслу equals не должен его учитывать.
          Иначе окажется, что два элемента Set'а, совершенно одинаковые по контенту, но разные по id (о котором мы даже ничего не знаем), окажутся не equals — бред же. Equals должен сравнивать значимый контент

          афтары как бы хотят нам сказать в документации, что эквивалентность мы должны задавать согласно «уникальному бизенс-ключу».
          Но откуда взять этот ключ они не рассказали

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

          короче, это косяк Хиба, он не должен был использовать стандартные equals и hashcode, а теперь уже легаси и поздно что-то менять


          1. asm0dey
            12.07.2016 17:41

            Почему же бред? Если id разные — значит в БД это разные записи, значит похожие, но разные сущности. И id-то вообще-то и явлется по сути единственны обязательным и уникальным идентификатором объекта. Я не говою, кстати, что его должна генерировать БД. Если хочется — можно его генерить саому, можно туда класть UUID или timestamp, всё что угодно.


            Или вы говорили про дефолтный id?


            1. olegchir
              12.07.2016 17:50

              потому что в этом подходе внезапно оказывается, что эквивалентных объектов вообще не существует
              я както немного не готов к такому повороту событий)


              1. asm0dey
                12.07.2016 17:53

                А так и есть — либо у вас есть бизнес-ключ либо искусственный. При наличии искусственного — только он определяет уникальность объекта, остальное на совести бизнес-логики.


      1. izzholtik
        12.07.2016 11:09

        1. а какие основания, кроме лютого легаси, для этого есть?

        6. каких, например? Пару раз писал standalone-софтины, юзающие JPA (EclipseLink), проблем с простреленными ногами вроде как не было…

        10. имеется в виду, что неаккуратно написанный equals может потянуть из БД тысячи вложенных сущностей? Или есть ещё какие-то проблемы?


        1. olegchir
          12.07.2016 11:21

          1. баги в Hibernate (в аннотациях тоже есть, но глючит), параметры недоступные аннотациями (блин забыл, пару месяцев назад было — хотелось переопределить создание проксей чтобы делать хитрые моки, и в XML это как-то делалось, а аннотациями — нет, вспомню — напишу). У хиба есть баги, которые они годами не закрывают, даже несмотря на то, что есть готовые патчи

          6. ну без нормального окружения не будет хотя бы JTA, верно? Нужно будет специально с этим пердолиться, например: https://spring.io/blog/2011/08/15/configuring-spring-and-jta-without-full-java-ee/ Ну там аннотации отвалятся, еще что-то. Не то чтобы этого нельзя было решить за вечер, поллитры водки и полпалки колбасы. Но он ведь хотел как проще и удобней, а отказавшись от преднастроенной среды application server'а, он как бы напротив бегает и собирает лбом все грабли. Есть готовые практики, как это всё должно работать, если ты далеко от них уходишь, не понимая что происходит — то страдаешь

          10. нормальный equals написать чудовищно сложно. Даже без всякого хибернейта, это просто теоретическая проблема ООП. Вот что сразу же гуглится по теме: http://www.artima.com/lejava/articles/equality.html


    1. olegchir
      11.07.2016 18:28

      15) операции типа «добавить» и «удалить» должны иметь или внятный результат, или внятный набор экзепшенов.

      addEntity возвращающее void, внутри себя проглатывающее экзепшен — это Очень Плохо. На экзепшене надо или экзепшен прокидывать дальше (и обречь пользователя на его проверку) или просто возвращать false

      16) хороший паттерн для операций с гарантированным исполнением и сокрытием ошибок: в самой первой строчке объявляется переменная result, в самой последней строчке она возвращается. Соответственно у функции только один вход, и только один выход. Никаких экзепшенов кроме критических ошибок и никаких return'ов между ними. Это позволяет читателям вашего кода не сойти с ума при попытке проанализировать ветви исполнения.

      если вам нужно негарантированное исполнение (может результат есть, а может и нет) — используйте Optional. Или встроенный Optional из Java8, или тот что в Guava в случае устаревшей джавы. А все проверки на null, size==0 и подобное нужно по возможности выкашивать — никогда не знаешь, когда бахнет.


      1. erlioniel
        11.07.2016 18:52
        +1

        Отличный ликбез. Можно я свои пять копеек вставлю?

        Коли уж речь зашла о Spring, то проще (и, как мне кажется, лучше) воспользоваться Spring Boot, а там уже просится Spring Data в которой уже есть практически все необходимые базовые CRUD-операции, а большинство более сложных кейсов можно сделать просто названием метода. Базовый туториал по последнему — http://projects.spring.io/spring-data/#quick-start

        Про 16 пункт отдельно — это спорная практика. То есть есть ее адепты, их аргументация понятна. Аргументация оппонентов тоже прозрачная — читать такой код порой очень сложно, потому что вместо выхода где-то вначале функции «переменная» тащится через всю функцию. Поэтому я бы не рекомендовал такой паттерн с разбегу, во всяком случае это повод для дискуссии, а не 100% работающая рекомендация.


        1. olegchir
          11.07.2016 21:03

          Про пункт 16 есть более подробное объяснение))

          Есть идея «абзацев». Абзац в коде — это какая-то сгруппированный исключительно по смыслу набор строк. Такой что в принципе, его можно было бы выделить в функцию, но не нужно, т.к. он не предназначен для реального переиспользования (а объявление функции — это как раз инструкция — ты должен это переиспользовать, а не писать велосипеды).

          Образно это как-то так:

          public int something:
          ____читаем из базы, читаем из базы, читаем из базы
          ____читаем из базы, читаем из базы, читаем из базы
          ____читаем из базы, читаем из базы, читаем из базы

          ____фильтруем, фильтруем, фильтруем
          ____фильтруем, фильтруем, фильтруем
          ____фильтруем, фильтруем, фильтруем

          ____пишем назад в базу пишем назад в базу пишем назад в базу
          ____пишем назад в базу пишем назад в базу пишем назад в базу
          ____пишем назад в базу пишем назад в базу пишем назад в базу

          Сама анатомия абзаца примерно такая:

          1) //Комментарий, что делает абзац, если там сложная магия
          2) Проверка предусловий и глобальных инвариантов
          3) Тело абзаца — основная работа
          4) Проверка постусловий и глобальных инвариантов
          5) Фиксация результата: в переменную, или в глобальный result

          ** Редактор хабры как-то не очень расположен к написанию в нем кода, поэтому тут надо включить фантазию **

          Так вот идея в том, что все fail-fast и быстрые результаты должны проходить строго по границам абзаца.
          Еще хорошо делать, чтобы все ресурсы (типа долгоживущих переменных) были объявлены до абзаца, и освобождены только после него.

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

          Метод целиком в каком-то смысле сам по себе является абзацем. Поэтому логично там в самом начале ставить глобальный result и возвращать его в конце, когда это реально удобно.


        1. Tealus
          12.07.2016 00:51
          +3

          С позиции новичка, так же как и автор, скажу по комментариям:
          Человек пытается разобраться с Hibernate. А вы в комментариях ему рекомендуете Spring, Tomcat, транзакции — вроде как это всё другие темы. Почему бы не рассмотреть только одно? Понятно, сейчас скажете, что оно всё используется всегда вместе, но вот захотелось разобраться с чем-то одним и без всякой магии. Вон этот ваш HibernateTemplate, который даже EntityManager позволяет скрыть, он же вроде как в основе всего этого Hibernate.


          1. olegchir
            12.07.2016 09:45
            +2

            1) Чтобы что-то использовать, не нужно в этом разбираться. Забросьте зависимости в pom.xml, напишите магические аннотации и радуйтесь :) Для того они и сделаны тащем-то

            2) Java в современном мире почти никогда не используется вне аппликейшен сервера (за исключением всякого эмбеддед, эклипс-платформы итп — но тогда ты скорей всгео не будешь юзать Хиб для MySQL!). Так что сделать минимальную war'ку и засунуть в аппликейшен сервер — это необходимое условие любого туториала по теме.

            Чувак решил разобраться с хибом и разобрался так, что пришел к неверным выводам. Ну хорошо что он вообще разобрался, конечно, а вот теперь идет работа над ошибками.

            2) Он УЖЕ использует транзакции. Но таким способом, когда надо в них разбираться и писать кучу кода. Аннотация @Transactional из Spring заменяет весь этот код, и в ней не нужно ничего понимать.

            3) «но вот захотелось разобраться с чем-то одним и без всякой магии»

            вот этот самая писечка.
            В его случае он хотел не «без магии», а просто запинать хоть что-то.

            А «без магии» в джаве не получится, тут всё — магия. Хибернейт весь целиком одна магия. От того что вы вручную напишите транзакции, магия рассеется чуть более чем никак. Как можно готовить магическое существо без магии?
            Тогда уж ему нужно было написать свой ORM… но тогда не было бы времени собственно делать дело, писать код

            9000) Если это статья человека, не разобравшегося в вопросе, то что она делает на Хабре в виде статьи? Кто-нибудь ее сейчас себе скопипастит в реальный код и начнется капец. Можно я тогда тоже начну сливать свои промежуточные брейндампы? Вот прямо сейчас пойду и солью пыщ пыщ на сто страниц какое говно логеры в жабе, это ок? :))


            1. asm0dey
              12.07.2016 11:23

              2) В современном мире микросервисов зачастую аппсервер не нужен, достаточно какого-нить рэтпака, основанного на нетти. А ещё часто встраивают аппсервер прямо в приложение: dropwizard, spring boot, wildfly swarm…


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


              1. olegchir
                12.07.2016 11:34

                внутри spring boot находится tomcat, а сам спринг внутри реализует всё нужно для ee жизни. Даже запускается это столько же времени — дофига времени. Чем это отличается от application server для пользователя (не для написателя стандартов)?

                про swarm не знаю, но судя по тому, как его котируют стандартофилы, там тоже полный фарш

                то есть мы наверное говорим об одном и том же, разница только как широко понимать слово «аппсервер»


                1. asm0dey
                  12.07.2016 12:00

                  Не обязательно томкат, можно и андертоу. Отличается для пользователя тем, что не надо собирать варник, да и не надо понимать на первом этапе что такое аппсервер и зачем надо. А если мы пишем ratpack приложение — то у нас и вовсе нет аппсервера никакого в приложении.


                  1. olegchir
                    12.07.2016 14:32

                    да, я тоже отчаянно ратую за Spring Boot. Но люди не хотят. Особенно админы, они не понимают как бут админить.


                    1. asm0dey
                      12.07.2016 14:35

                      Так вы до конца идите, почему только бут? Надо вместе с докером и чем-нить типа fabric8 продвигайте. Как раз под тем соусом, что настройки всегда одинаковые с точностью до адресов, балансируется он автоматически и вообще минимум работы на них в плане администрирования приложений.


            1. yroman
              12.07.2016 21:29

              >>Аннотация @Transactional из Spring заменяет весь этот код, и в ней не нужно ничего понимать.

              Ну да, конечно, не нужно. А потом человек начнет пихать эту аннотацию где только можно и прострелит себе ногу.

              https://www.ibm.com/developerworks/library/j-ts1/


              1. olegchir
                14.07.2016 20:33

                Вы не поверите: большинство новичков весьма смутно представляют себе, что такое транзакция, не говоря уж об уровнях изоляции (этого зачастую не знают middle разработчики с опытом). И тем не менее успешно пользуются ими. Достаточно выставить дефолты :-)


      1. asm0dey
        11.07.2016 19:31
        +1

        Считаю что один вход и один выход — плохая идея. fail-fast может и чиатется чуть хуже, зато работает чуть лучше.


        1. erlioniel
          12.07.2016 08:53

          Это очень ситуативная штука. В некоторых ситуациях лучше работает одно, в некоторых другое. В любом случае я бы не пытался все методы делать в одном ключе в этом плане.


    1. olegchir
      11.07.2016 21:43

      17. Не используйте синглтон типа synchronized accessor. Это наиболее глупый синглтон, потому что тормозит в самом типовом сценарии использования, а профита с этого никакого не имеет.

      Другие варианты синглтонов есть, например, здесь: https://habrahabr.ru/post/129494/


  1. oaev
    11.07.2016 18:04

    интерфейс org.hibernate.Session наследует AutoCloseable, поэтому можно использовать try-with-resources и избежать этого finally


  1. erlioniel
    11.07.2016 18:54

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


  1. Stalker_27
    11.07.2016 22:14

    Уважаемые комрады, можно и мне вставить 5 копеек. Не совсем по теме, но по существу дела. Я просто процитирую, чтобы сложить мозаику… ну и может подсказку получу…

    1. Так вот. Здесь написан лютый ад. Который приведет к куче проблем. Мы тут поревьюили этот туториал, и никак не оправимся от шока, кровь стынет в жилах. В 2016 году всё это делается немного по-другому…

    PS — Отлично, все скорее всего так и есть, по сему спорить и не буду. Но, как писал выше, отталкивался от того, что нашел…

    2. Возможно, стоит написать книжку по Hibernate на русском языке, где такие вопросы будут подробно рассмотрены?

    PS А вот тут, смотри позицию ниже за номером 3.

    3. По вопросу передачи знаний — обычно оно передается от учителя к ученику в джава-компаниях. Код, в котором сто сорок раз скопипастана кривая обертка запуска функции в транзакции, просто не пройдет первое же ревью. Там достаточно всего несколько дней, чтобы набить шишки…

    PS То есть, отлавливать чужую практику в нормальном режиме без попадания в одну из адекватно — практикующих организация практически бесполезно (тут даже уже пошло взаимо-копание среди «профи» в стиле, в какой руке нужно держать солонку, когда солишь яичницу). Это тоже хорошо, в споре — истина. Но вопрос проще — а где еще набивать руку, если конторы хотят хоть с каким то опытом, а тьюторить им смысла нет, ибо учат только своих…

    Я с большим бы удовольствием попал в организацию, где можно начать с начала и правильно и «набить шишки...» или набить «правильные» шишки, но спотыкаюсь о порог вхождения. Может кто подскажет мне с этим вопросом?


    1. asm0dey
      11.07.2016 22:17

      Не идти в конторы, где Hibernate является обязательным, идти туда, где он является дополнительным плюсом. Потому что там где он обязателен (когда-то у меня такое было) спрашивают всякие сложные штуки (я спрашивал) — уровни кеширования, SELECT NEW, разные API Для построения запросов, наследование…


      1. Stalker_27
        11.07.2016 22:28

        Я еще упрощу вопрос — я не могу попасть на джуно-вакансию не по причине отсутствия супер-опыта по Хиберу или Спрингу условно, а просто потому, что не хотят браться...(ну или брать). Типа с «привнесенным опытом», нерелевантным. https://ru.linkedin.com/in/павел-губарев-38a318120 — открываю карты… Не могу проломить стенку даже на «подмастерья»… раз уж в данной отрасли сложилась такая схема.


        1. asm0dey
          11.07.2016 22:35

          Боюсь что это вопрос везения — когда-то я имел нерелевантный опыт тоже и устроился джуном. Но я вообще не программировал к тому моменту.
          А вы, так как программировали, имеете ещё один путь — контрибутить в opensource java проекты на гитхабе и поместить ссылку на свой профиль гихабовский в резюме.


          1. Stalker_27
            11.07.2016 22:39

            Можно узнать, сколько Вам тогда было? И если начну фрилансить… ну были и такие советы, то кроме упрочнения своих ошибок я ничего нового не привнесу, даже если это будет работать. Мой один товарищ на этом уже поймался.
            Пока делал сам на сам, клиент не видит, что внутри — работает и ладно. А когда решил устроиться куда посерьезнее, то получил по ушам на грани вылета с испытательного. Хотя у него и вполне настоящий сертификат Специалиста по 1С.


            1. asm0dey
              11.07.2016 22:49

              Было в районе 23 лет (может чуть меньше). Если начать фрилансить — то получится чисто индусское программирование, не стоит. А могу узнать в каком вы городе находитесь?


              1. Stalker_27
                11.07.2016 23:02

                Пятигорск, ну а потому, что мне не 23 — мне несколько (это мягко сказано) сложнее…


            1. asm0dey
              11.07.2016 22:53

              Вот мне сходу вакансия попалась где знаний Java не нужно


              1. Stalker_27
                11.07.2016 23:03

                Цитирую… «приглашает студентов последних курсов технических ВУЗов пройти обучение по JAVA с возможностью последующего трудоустройства в штат компании». Это не просто БОРТОВКА, а 100% гарантия посыла в эро-путешествие…
                Это заканчивается вот так…
                Большое спасибо за Ваш интерес, проявленный к нашей открытой вакансии «Младший разработчик». К сожалению, в настоящий момент мы не готовы сделать Вам это предложение. Мы внимательно ознакомились с Вашим резюме, и, возможно, вернемся к Вашей кандидатуре, когда у нас возникнет такая потребность.


                1. asm0dey
                  11.07.2016 23:11

                  Ну на том же career.ru есть ещё вакансии. Энивей пока не пошлёшь резюме с сопроводительным письмом — не узнаешь :)


          1. Stalker_27
            11.07.2016 22:47

            Да и строить план на основе везения — это как планировать траты на след год под шанс выиграть в лотерею… как то так. Меня такая стратегия смущает, гораздо более чем то, что придется «пахать»…


    1. asm0dey
      11.07.2016 22:18

      Ну и да, по Hibernate есть отличная книга


    1. olegchir
      11.07.2016 22:27

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

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

      аккуратно представьте, что вы хотите получить, например, через 1 год, или через 3 года. Кем вы хотите стать, что уметь, и так далее. Не те «через 3 года» что спрашивают на собеседовании, а на самом деле. Потом сделайте diff между желаемым и действительным (что вы умеете сейчас) — и вот у вас есть стратегический план развития. Очень простой алгоритм.

      желаемое можно описать здесь, может кто подскажет, что именно вам нужно изучать (если ваша проблема именно в нехватке источников информации)


      1. Stalker_27
        11.07.2016 22:34

        Я не сомневаюсь в Вашей компетенции, но Вы смотрите на ситуацию с позиции «профи», поэтому немного тяжело общаться. Оно ведь как, у одного печеньки вчерашние, а у кого сухарь с плесенью вызывает озабоченность. Вопрос был поставлен с позиции того, что я не ставлю желание попасть вот именно туда, вот прям куда хочу и где всё будет так, как мне нравится… это правильно, но это история более далеких времен из будущего. Если оно когда то наступит… Пока я сделал вывод, что заниматься «самокопанием» — это большая потеря времени с низким КПД… а прорваться на стартовую позицию — тоже проблематично. Да, в примере все не идеально, и будь оно… ну условно тестовым заданием, «закопали бы в асфальт».
        А где выход?


        1. olegchir
          11.07.2016 23:02

          > Пока я сделал вывод, что заниматься «самокопанием» — это большая потеря времени с низким КПД…

          Очень точно сказано. Да.

          Более того, если вы сидите в пещере (или в гараже) и в одиночку медетируете на высокое просветление «умения программировать на джаве», то просветление это возможно даже придет — а рынку оно будет не нужно. Помните начало винрарного рассказа «так говорил заратустра»? :)

          Вы ведь хотите на джаве писать не потому, что получите за это филдсовскую, а потому что хотите много денег и интересной комфортной работы? Плюшки дают за выполнение потребностей рынка -> нужно обучаться не просто чему-то абстрактно, а тому, что потребно на рынке -> нужно от этого рынка иметь постоянную обратную связь. Нельзя одновременно безвылазно медитировать в пещере и иметь обратную связь от внешнего мира :)

          Есть опыт реабилитации людей «из пещеры» и «из гаража». Это очень долго и больно для всех участников. Просто не нужно туда залезать.

          > но Вы смотрите на ситуацию с позиции «профи»

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

          > я не ставлю желание попасть вот именно туда, вот прям куда хочу и где всё будет так, как мне нравится…

          а вот и зря не ставите :)

          вы поймите в чем чисто бытовой смысл… Человек — это по сути сложная система для решения задач с обратной связью. Вы как преподаватель алгоритмов должны понимать, как это устроено:
          — вначале идет фаза планирования (что ты хочешь сделать),
          — потом фаза исполнения (реально что-то делаешь),
          — потом фаза проверки (действительно ли мы добились чего хотели),
          — потом фаза корректировки плана.
          — и всё начинается заново по кругу

          Plan, Do, Check, Act. https://en.wikipedia.org/wiki/PDCA

          Если вы пропускаете какой-то из этих этапов, система с обратной связью не работает. Это не какой-то профессиональный снобизм, это просто свойство нашего мира :)

          Если вы не ставите себе четкой цели, ваш план действий (или ваша карьера, или что-то еще), просто не рабоает.

          Точнее, работает, но с ОЧЕНЬ низким КПД. Вы действительно настолько богаты, чтобы так разбрасываться своими силами? У вас столько свободного времени, столько лет жизни осталось?

          > это правильно, но это история более далеких времен из будущего

          То есть вас не устраивает размер временного диапазона, который надо запланировать? Не хватает мощности планировщика?

          Ну окей, давайте запланируем не 3 года, а 3 месяца. Расскажите нам, чего вы добьетесь и чего изучите через три месяца.

          Если вы не знаете чего хотеть, то тут есть два варианта:
          1) Мы можем рассказать, что вам положено хотеть достичь через 3 месяца. Например, какие вещи касательно Java изучить.
          2) Вы можете сами провести исследование и разобраться, чего нужно вам (или скорее — чего нужно рынку, если вы хотите рубить на этом бабло)


          1. Stalker_27
            11.07.2016 23:12

            Я не против начать с нуля — но моя персона не очень интересует кого, даже с учетом «Вы как эксперт по алгоритмам намного более лучший потенциальный программист, чем был я.»

            Насчет «Есть опыт реабилитации людей «из пещеры» и «из гаража». Это очень долго и больно для всех участников. Просто не нужно туда залезать».
            Ну когда я туда «залез» — многих контор вообще не было еще на свете, и как бы я не рассчитывал что через 15 лет работы всех поставят к стенке в угоду оптимизации. Если вы умеете видеть на 15 лет вперед — я признаю вас не просто Гуру, это уровень Бога…

            У вас столько свободного времени, столько лет жизни осталось?.. согласен и всеми конечностями «ЗА».
            Но тут получается… я даже не знаю как это правильно описать алгоритмически — скорее всего рекурсия без условия выхода… Хочешь научиться водить — иди таксуй, но чтобы таксовать — нужны права, а чтобы получить права — научись водить… количество повторов — по вкусу…

            А выхода здесь два — или не заходить, или все таки разорвать порочную практику «нерелевантного» опыта.


            1. olegchir
              11.07.2016 23:27

              Блин, всё проще. Учите 1) базовую Java 2) Spring 3) Hibernate 4) основы создания веб-интерфейса — и идёте джуном в нормальную контору работать. Или развивайтесь в текущей, используя обратную связь от коллег. Это не план уровня бога, а очень четкая задача с понятным условием выхода и ограничениями по времени.

              Если нет подкованых в джаве коллег, в качестве изначальной обратной связи можно использовать хабр или, например, фейсбук (кстати, если вы там есть, добавляйтесь: https://www.facebook.com/olegchir).

              Меня очень вдохновляют видео с https://www.youtube.com/user/JUGRuVideo
              Если живете в Москве/Питере, участвуйте в их мероприятиях
              Если нет — хотя бы прорабатывайте видео.
              Это весьма свежая и релевантная информация, поэтому ее можно считать обратной связью живой системы
              (Но есть нюанс: там многие видики расчитаны на очень продвинутую аудиторию. Если вы уже прорвались через порог входа, то можете просто например, потихоньку прорабатывать, о чем там люди говорят)


              1. erlioniel
                12.07.2016 09:08

                Кармы не хватает голосовать, поэтому поддержу комментарием и немного дополню.

                Павел, вы там выше ушли в полную защиту из серии «Откуда мне знать как правильно, я же новичек». Это понятно, но абсолютно не конструктивно. Олег хоть и жестко, но описал более-менее все основные проблемы, которые стоит решить в этом примере.

                Вместо того чтобы сокрушаться что опыт нельзя получить без работы, а работу без опыта — у вас есть отличная возможность скорректировать свои знания относительно рынка, поработать над кодом из топика и получить фидбек от людей, которые реально в этой индустрии работают. Почему вы этим не хотите воспользоваться?


                1. asm0dey
                  12.07.2016 11:17

                  Вы не на том уровне ответили, поэтому не факт что Павел ваш комментарий увидит.


                  1. Stalker_27
                    12.07.2016 16:38

                    Да вижу я…


  1. KEKCoGEN
    12.07.2016 16:39

    Поддерживаю комментаторов выше. Такой код не проходит код ревью. А в местах где проходит лучше не работать.