вторник, 4 декабря 2018 г.

Hibernate & JSON type in PostgreSQL

Что, если у нас в базе данных PostgreSQL есть столбец, содержащий данные типа JSON, но нет ни соответствующего ему типа данных в Java, ни механизма преобразования в Hibernate из какого-то встроенного/пользовательского типа/объекта Java в JSON и обратно?

Тогда у нас есть 2 варианта*:
1) создать самим механизм преобразования JSON-String Java в JSON PostgreSQL;
2) использовать разработанную Vlad Mihalcea библиотеку hibernate-types-52.

Первый вариант:

1\ Создаём свой диалект PostgreSQL:

public class JsonPostgreSQLDialect extends PostgreSQL9Dialect {

    public JsonPostgreSQLDialect() {
        super();
        this.registerColumnType(Types.JAVA_OBJECT, "json");
    }
}

2\ Указываем наш диалект в файле application.properties:

spring.jpa.properties.hibernate.dialect=ru.epatko.postgresjson.stringjsontype.JsonPostgreSQLDialect

3\ Создаём свою реализацию интерфейса org.hibernate.usertype.UserType. Ниже приведена упрощённая реализация данного интерфейса, т.к. класс будет "заточен" для мапинга объекта только одного типа (java.lang.String):

public class StringJsonUserType implements UserType {
    @Override
    public int[] sqlTypes() {
        return new int[] {Types.JAVA_OBJECT};
    }

    @Override
    public Class returnedClass() {
        return String.class;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        if(x == null)
            return y == null;
        return x.equals(y);
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }

    @Override
    public Object nullSafeGet(ResultSet resultSet, String[] names, SharedSessionContractImplementor session, Object owner)
            throws HibernateException, SQLException {
        return resultSet.getString(names[0]);
    }

    @Override
    public void nullSafeSet(PreparedStatement statement, Object value, int index, SharedSessionContractImplementor session)
            throws HibernateException, SQLException {
        if (value == null) {
            statement.setNull(index, Types.OTHER);
        } else {
            statement.setObject(index, value, Types.OTHER);
        }
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    @Override
    public boolean isMutable() {
        return true;
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return (String) value;
    }

    @Override
    public Object assemble(Serializable cached, Object o) throws HibernateException {
        return cached;
    }

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return original;
    }
}

4\ Аннотируем объект и соответствующее поле аннотациями @TypeDefs и @Type:

@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@TypeDefs({@TypeDef(name = "StringJsonObject", typeClass = StringJsonUserType.class)})
public class MyModel {

    @Id
    private long id;

    @Type(type = "StringJsonObject")
    private String details;            //String of JSON object
                                       //Example: "{\"value1\":1,\"value2\":\"abc\"}"
}

5\ Создаём обычную реализацию CrudRepository.

@Repository
public interface MyRepository extends CrudRepository<MyModel, Long> {
}

Второй вариант:

1\ Подключаем зависимость от библиотеки hibernate-types-52:

<dependency>
  <groupId>com.vladmihalcea</groupId>
  <artifactId>hibernate-types-52</artifactId>
  <version>2.3.5</version>
</dependency>

2\ Мне потребовалось подключить также зависимости от библиотек jackson-core, jackson-databind и jackson-annotations:

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>${jackson.version}</version>
</dependency>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>${jackson.version}</version>
</dependency>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>${jackson.version}</version>
</dependency>

3\ Объявляем новые типы с помощью аннотации @TypeDefs над классом-Entity.
С помощью аннотации @Type над нужным полем класса прописываем мапинг поля в JSON:

@Data
@Entity
@TypeDefs({
        @TypeDef(name = "json", typeClass = JsonStringType.class),
        @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
@NoArgsConstructor
public class MyNewModel {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Type(type = "jsonb")
    @Column(columnDefinition = "json")
    private Details details;

    public MyNewModel(Details details) {
        this.details = details;
    }
}

4\ Создаём обычную реализацию CrudRepository:

@Repository
public interface MyRepository extends CrudRepository<MyNewModel, Long> {
}

5\ Тестируем:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyRepositoryTest {

    @Autowired
    private MyRepository repository;

    @Test
    public void saveModel() {

        String name = "name";
        String email = "email";
        int age = 1;
        Details details = new Details(email, name, age);
        MyNewModel model = new MyNewModel(details);

        MyNewModel saved = repository.save(model);

        assertEquals(saved, model);
    }
}

6\ В базе данных находим вот такую запись:


Весь код можно посмотреть в репозиториях на GitHub:
- вариант 1;
- вариант 2.

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

Грокаем алгоритмы

В "Грокаем алгоритмы. Иллюстрированное пособие для программистов и любопытствующих" (Адитья Бхаргава) простым доступным языком объясняется работа нескольких наиболее популярных алгоритмов, в том числе и тех, что применяются в машинном обучении.

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

Книга, купив которую, не пожалел ни разу

Бывает такое: покупаешь книгу - вроде бы интересная и даже про то, что должно пригодиться в работе, но читаешь её и не понимаешь, где именно в своём проекте ты можешь применить эти знания. Либо случаи в книге описываются очень специфические, либо слишком уж упрощённые примеры кода приводятся. В общем, не "заходит" книга.

Так вот,  "Java Persistence API и Hibernate" (Бауэр Кристиан, Гэвин Кинг, Гэри Грегори) не из таких. На каждой её странице можно бить себя ладонью по лбу и восклицать: "Вот оно! Вот, как надо! Ну, теперь уж точно всё будет красиво!" Почти на каждую страницу можно приклеивать закладку с тремя восклицательными знаками, чтобы не забыть, куда посмотреть перед тем, как начать кодить.

Эта книга - букварь Hibernate, в котором от А до Я рассказывается всё: от азов отображения объектов на схему реляционной базы данных, до производительности запросов. Пока что не видел ни одного видео-курса, который бы так полно охватывал и подробно раскрывал столько тем по JPA и Hibernate.

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

В общем, читать - обязательно. Правда, есть вероятность, что вас перестанут понимать коллеги по работе. )

воскресенье, 2 декабря 2018 г.

Что дальше

О чём планирую писать в блоге дальше? О программистских буднях, конечно же. О прочитанных книжках.
За год с момента приезда в Москву прочитал около 10 - 15 книг по программированию. Начну потихоньку выкладывать здесь мысли и идеи из них, которые показались мне интересными.

Всё ещё продолжаю слушать тематические подкасты.

На митапы и конференции ни на одну не сходил - дорого и пока хватает видеозаписей с них на YouTube.

Отзывы в Интернете о потенциальном работодателе

Не стоит пугаться, если в Интернете вы прочитаете "страшные" отзывы об условиях работы у потенциального работодателя. Зачастую, такие отзывы пишут люди, чем либо обиженные на компанию.

Как о прошлом месте моей работы, так и о текущем прочитал в интернете несколько негативных отзывов о "нечеловеческих" условиях работы, о "плохом" коллективе и т.д. В обоих случаях эти отзывы не соответствовали действительности. Оказывалось, что и люди работают нормальные и условия работы преотличные.

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

Как я нашёл следующее место работы

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

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

До запланированного и уже оплаченного мной отпуска оставалось 2 недели, заняться на работе было уже нечем и я, "стряхнув пыль" с резюме, занялся поисками нового места работы.

С удивлением обнаружил, что могу теперь и сам выбирать, в какой компании хочу работать. Иногда даже делаю такой выбор неосознанно. Например, в одном из банков была очень гнетущая атмосфера для работы: проверки и перепроверки на входе, строгий режим работы от и до, полутёмные коридоры, помещения без окон и вентилляции (только с кондиционером), тишина как в склепе, внутри не встретил ни одного человека, кроме провожавшего меня HR-а, запрет на выход в интернет с рабочего компьютера и др. Как результат: не смог написать простейшего приложения на java core, мысли путались, в голове всё время вертелось: "как я буду тут работать? здесь же как в тюрьме..." Собеседование провалил и мне даже не перезвонили, чтобы сказать об этом ))

Отказался от нескольких предложений о работе на поддержке приложений, использующих устаревшие технологии (jBoss, javaEE 6). Также отказался от работы в компаниях, где пытались хитрить с моей будущей зарплатой:
- 50% - оклад и 50% - премия, которая будет уплачиваться, начиная с третьего месяца работы и раз в квартал (ну, т.е. вторую половину зарплаты я, может быть, увижу не раньше, чем через полгода после начала работы);
- заявленная мной сумма - это gross (до удержания налогов), а net (после удержания налогов) будет меньше;
- и т.д.

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

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

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



Что делал на первой работе

На первой работе столкнулся с целым зоопарком технологий:
Lua, Tarantool, Apache Kafka, Telegraf, Clickhouse, Cassandra, Redis, InfluxDB, PostgreSQL, Hibernate ORM, JDBC, Apache Maven, Gradle, Git, Gitlab CI, JUnit, Mockito, Spring, Spring Boot, HTML5, JavaScript, CSS3, Python, Docker.
Всё это активно использовалось в микросервисах, которые разрабатывались и поддерживались мной.
Отдел, в котором я работал, назывался "экспериментальным" и занимался изучением и проверкой применимости новых (хайповых) технологий в продуктах компании.
Пришлось изучать много нового. Даже с machine learning довелось поработать.
Было интересно, весело, но... компания обанкротилась. 
Расстались мы с компанией нормально. И я снова вышел на рынок труда в поисках следующего места работы.

Как я обустраивался в Москве

До того, как вылететь в Москву, я нашёл через сайт Авито ближайший к работе хостел и забронировал в нём место на 1 месяц (до первой зарплаты).
После первой зарплаты снял квартиру в 45 минутах езды на метро до места работы.
Чем сфера услуг в Москве отличается от аналогичной в небольших городах: тут никто ни за что не отвечает. На собственную репутацию как производителя услуг всем начхать. Москва большая, если не ты, то кто-нибудь другой обязательно услуги закажет.
Очень сильно это заметно в сфере риелторских услуг. Помня о том, как подбирают квартиры риелторы в г.Владивостоке, я, поморщившись, обратился в риелторское агентство в Москве. Названия этого агентства не помню, перечислять всё то хамство, которого я от них натерпелся, не буду. Скажу в кратце итоги:
1\ до того как получили от меня деньги, они уверяли:
- что вот эта самая квартира, вот по этой цене сейчас сдаётся и мне дадут телефон хозяев как только я оплачу услуги агентства,
- что риелторы будут персонально подбирать мне квартиры и чуть ли не сопровождать на просмотры;
2\ как только я перечислил им деньги, вяснилось следующее:
- я заплатил абонентскую плату за годовое пользование сайтом риелторского агенства для самостоятельного поиска квартиры;
- на своём сайте агентство собирает объявления о сдаче в аренду квартир с 2-3 общедоступных бесплатных сайтов;
- мне никто ничего не должен и денег я назад не получу;
- я свободен )))
3\ через пол года, когда я решил сменить квартиру, я не смог зайти на сайт этого риелторского агентства. Объяснили мне это следующими причинами:
- я же уже нашёл квартиру пол года назад;
- когда же я напомнил им, что, согласно договору, заплатил за год пользования их сайтом, тут же возникли "технические трудности, из-за которых нет никакой возможности восстановить мне доступ".

В общем, вы понимаете, что пользоваться услугами риелторских агентств в Москве категорически противопоказано. ))

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

Как я нашёл работу

Всего на поиски работы у меня ушло около 1 месяца.
Работа есть для всех. Это факт.
Работу получит каждый, кто сможет изучить технологии, необходимые, согласно размещённой вакансии. Это тоже факт.
Поэтому, парни и девушки, отбрасываем все рефлексии на тему "я не такой(ая), меня не возьмут" и действуем. Не боги горшки обжигают.
Работу на минимально нормальную зарплату можно найти даже без опыта коммерческой разработки.
Шансы на получение работы значительно увеличиваются, если у вас есть какие-нибудь проекты на GitHub, написанные с использованием технологий, перечисленных в объявлении о вакансии.
Большим минусом курсов программирования является их ограниченность. Ограниченность по времени прохождения курсов, по рассматриваемым на курсах технологиям, по объёму/сложности решаемых практических задач. Эти недостатки в своём образовании Вам придётся устранять самостоятельно.
Как это сделать? Очень просто: находите в google 20/30/40/50 идей для самостоятельных проектов на Spring boot, покупаете книгу по микросервисам на Spring boot и, с оглядкой на код в книге, пишете свой проект и выкладываете на GitHub.
Параллельно, можно прорешивать задачи из разряда "для собеседований". Информации по таким задачам в google тоже масса.

За месяц я прошёл около 20 собеседований. Резюме рассылал на hh.ru по 40 - 80 штук в день.

Основное правило: первые собеседования - всегда провальные. Не нужно проходить первыми собеседования в те компании, в которых вам больше всего хотелось бы работать. Второй шанс попробоваться туда может представиться не скоро. А может вообще не представиться.

Как бы то ни было, первую работу я нашёл. На всякий случай получил от работодателя письмо о том, что он меня ждёт. Продал машину, простился с друзьями и родственниками и вылетел в Москву.

Первая моя зарплата была 80 т.р. в месяц, с условием, что поднимут до 100 т.р. через пол года. Но за пол года, учитывая все мои заслуги, мне подняли зарплату до 150 т.р. в месяц.

Как я уволился с работы

Когда до окончания учебно-боевого проекта на Spring boot оставалось совсем немного, мне окончательно расхотелось работать в адвокатуре.
Произведя для очистки совести несложные расчёты, я пришёл к следующим выводам:
- до трудоустройства программистом мне осталаось 1 -2 месяца (даже уже не вспомню, на чём было основано моё убеждение);
- кредитов, ипотек и других долгов у меня нет, а, значит, последней адвокатской зарплаты при условии экономии должно хватить на эти самые 1-2 месяца. На крайний случай, решил я, найду где-нибудь работу сутки-через-трое, которая позволит мне прожить за пределами этого срока и нормально искать работу.
Положил начальству на стол заявление об увольнении. Направил в Адвокатскую палату Приморского края заявление о прекращении статуса адвоката. Переехал с арендованной квартиры в дом к сестре и свояку в п.Тавричанка.
Живя у сестры со свояком, бегал каждое утро, днём пару часов занимался физкультурой, а всё оставшееся с утра до вечера заполнял пробелы в знаниях, необходимых для удачного прохождения собеседования.

Как я взял себя в руки

На "каникулах" я находился примерно 2 или 3 месяца.
За это время я успел 5 раз сходить на гору Пидан, исходить вдоль и поперёк о.Русский и отдуши накупаться в Японском море.
Потом внутри появилась какая-то пустота и стало грызть чувство вины: я уже изучил так много в Java core, осталось сделать всего пару шагов к мечте, а я вместо этого всё ещё хожу на опостылевшую работу и занимаюсь давно утратившими смысл вещами.
Взяв себя в руки, прошёл оставшиеся темы по курсу, выпросил у Петра (основатель учебного курса) в работу проект на Spring boot и стал мониторить вакансии на рынке труда.

I'm back

Я возвращаюсь!
Не прошло и 1,5 года с того момента, как я ушёл на каникулы, и я уже возвращаюсь.
Почему так долго не писал? Было не до того. Жизнь крутилась-вертелась и события в ней менялись с такой скоростью, что не было времени не то, что блог вести, но даже просто остановиться и отдышаться. Что поделать? Мир высоких технологий живёт на запредельных скоростях.
Не. Всё вру, конечно же. Было и время и мысли посещали: а не продолжить ли вести блог. Но не было какой-то мотивации.
Сейчас мотивация появилась. Я попал на неделю в больничку и у меня было достаточно времени подумать обо всём, вспомнить, что произошло за эти 1,5 года и прийти к мысли о том, что неплохо было бы свои "умные" и "оригинальные" мысли где-то выложить.
И так, продолжим...