Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
onixred
7 лет назад

Как подружить Postgres Network Address Types и hibernate

Всем привет, сегодня я расскажу как подружить Postgres Network Address Types c hibernate.

Наверное все понимают что для хранкния мак адреса в PostgresDB использовать varchar это не совсем правильно. Когда есть специальный тип macaddr.
В проекте использовался hibernate 3.6.7.Final.

Сначала я попробовал просто запустить проект вот с такой реализацией:

/**
 * Сущность для таблицы  mac_adr
 * 
 @author <a href="mailto:onixbed@gmail.com">amaksimov</a>
 */
@Entity
@Table(name = "mac_adr")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@SequenceGenerator(name = "sequencePooledMacEntity", sequenceName = "mac_pooled_seq")
public class MacAdrEntity {

    private Long id;

    private PgMacaddr mac;

    @Id
    @GeneratedValue(generator = "sequenceMacAdrEntity")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Column(name = "mac")
    public PgMacaddr getMac() {
        return mac;
    }

    public void setMac(String mac) {
        this.mac = mac;
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    @Transient
    public MacAddress getDTO() {
        MacAddress ret = new MacAddress();
        try {
            ret.setMacString(mac.getMac());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return ret;
    }
}

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

column "mac" is of type macaddr but expression is of type character varying

Ок я решил указать явный каст

    @ColumnTransformer( write="macaddr(?)" )
    @Column(name = "mac")
    public PgMacaddr getMac() {
        return mac;
    }

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

select macAdrEntity from MacAdrEntity as macAdrEntity  where macAdrEntity.mac in (:macAddresses)

И тут началось .... я испробовал не один вариант но хибернет кидал одну ошибку за другой. Я уже подумал лучше использовать строку в БД.
Наконец я нашел еще один вариант на просторах интернета:

    @ColumnTransformer(read = "CAST(mac AS varchar)", write="macaddr(?)" )
    @Column(name = "mac")
    public PgMacaddr getMac() {
        return mac;
    }

Но увы он тоже не помог мне, просто выкинул ошибку:

org.hibernate.QueryException: illegal attempt to dereference collection ...

Тогда я решил создать свой класс для macaddr

/**
 * Postgre тип macaddr
 *
 @author <a href="mailto:onixbed@gmail.com">amaksimov</a>
 */
public class PgMacaddr implements Serializable {

    private static final long serialVersionUID = 1L;

    private String mac;

    public PgMacaddr() {
        this.mac = null;
    }

    public PgMacaddr(String mac) {
        this.mac = mac;
    }

    public String getMac() {
        return mac;
    }

    public void setMac(String mac) {
        this.mac = mac;
    }
}

Тут все просто создаем пользовательский тип отдельный из которого будет преобразован Postgres тип macaddr и на оборот.
Теперь самое сложно нужно создать класс преобразования.

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
import org.postgresql.util.PGobject;
import org.springframework.util.ObjectUtils;

/**
 * Конвертация Postgre типа macaddr в java объект {@link PgMacaddr}
 * 
 @author <a href="mailto:onixbed@gmail.com">amaksimov</a>
 */
public class PgMacaddrType implements UserType {

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

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        if (value == null)
            return null;
        else {
            PgMacaddr PgMacaddrNew = new PgMacaddr();
            PgMacaddr PgMacaddrOriginal = (PgMacaddr) value;

            PgMacaddrNew.setMac(PgMacaddrOriginal.getMac());

            return PgMacaddrNew;
        }
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        Object deepCopy = deepCopy(value);

        if ((deepCopy instanceof Serializable))
            return (Serializable) deepCopy;

        return null;
    }

    @Override
    public boolean equals(Object arg0, Object arg1) throws HibernateException {
        return ObjectUtils.nullSafeEquals(arg0, arg1);
    }

    @Override
    public int hashCode(Object arg0) throws HibernateException {
        if (arg0 != null)
            return arg0.hashCode();
        else
            return 0;
    }

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

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, Object arg2)
            throws HibernateException, SQLException {
        PgMacaddr mac = null;

        String strMac = rs.getString(names[0]);

        if (strMac != null) {
            mac = new PgMacaddr(strMac);
        }

        return mac;
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index)
            throws HibernateException, SQLException {
        if (value == null) {
            st.setNull(index, Types.VARCHAR);
        } else {
            PGobject pgObj = new PGobject();
            pgObj.setType("macaddr");
            pgObj.setValue(((PgMacaddr) value).getMac());
            st.setObject(index, pgObj);
        }
    }

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

    @Override
    public Class<PgMacaddr> returnedClass() {
        return PgMacaddr.class;
    }

    @Override
    public int[] sqlTypes() {
        return new int[] { Types.OTHER };
    }
}

Не много раскажу подробнее о методах:

  • assemble — метод преобразовывает в объект для хранения в кэше.
  • deepCopy — метод реализует полное копирование объекта.
  • disassemble — метод восстанавливает объект из кэша в Serializable объект.
  • equals — метод сравнивает 2 объекта на вавенство.
  • hashCode — метод возвращает хеш код
  • isMutable — метод возвращает признак мутации объекта
  • nullSafeGet — метод преобразовывает значение из БД в java объект.
  • nullSafeSet — метод преобразовывает java объект в БД объект для записи.
  • replace — метод вносит изменения в старый объект из нового.
  • returnedClass — метод возрощает тип класс, методом nullSafeGet.
  • sqlTypes —тип колонки в БД.

Отлично создаем аналогичне классы для inet, cidr и у нас полный комплект. Теперь наш проект поддерживает сетевые типы

Всем пасибо за внимание не забывайте подписываться на мой блог. =)

38
3.832 GOLOS
На Golos с December 2016
Комментарии (7)
Сортировать по:
Сначала старые