package org.example.demo.hibernate;

import java.time.Period;

import javax.persistence.AttributeConverter;
import javax.persistence.Convert;
import javax.persistence.Converter;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;

public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager em = factory.createEntityManager();

        Event event = new Event();
        event.span = Period.ofDays(2);

        em.getTransaction().begin();
        em.persist(event);
        em.getTransaction().commit();

        em.clear();

        Event e = em.find(Event.class, event.id);
        // P2D
        System.out.println(e.span);

        em.close();
        factory.close();
    }

    @Entity(name = "Event")
    public static class Event {
        @Id
        @GeneratedValue
        Integer id;
        @Convert(converter = PeriodStringConverter.class)
        Period span;
    }

    @Converter
    public static class PeriodStringConverter implements AttributeConverter<Period, String> {
        @Override
        public String convertToDatabaseColumn(Period attribute) {
            return attribute.toString();
        }

        @Override
        public Period convertToEntityAttribute(String dbData) {
            return Period.parse(dbData);
        }
    }
}

生成 SQL

drop table if exists Event cascade
drop sequence if exists hibernate_sequence

create sequence hibernate_sequence start 1 increment 1
create table Event (id int4 not null, span varchar(255), primary key (id))
select nextval ('hibernate_sequence')
insert into Event (span, id) values ('P2D', 1)

select test_event0_.id as id1_0_0_, test_event0_.span as span2_0_0_ from Event test_event0_ where test_event0_.id=1

AttributeConverter 转换的类,需要至少定义 equals and/or hashCode ,否则 Hibernate 会报 WARN 参见 org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry.checkEqualsAndHashCode(Class)

如果使用 AttributeConverter 的 attribute type(Java type) 是不可变的(immutable)则 basic type(Hibernate type) 也是不可变的,如果 attribute type(Java type) 是 mutable 则 basic type(Hibernate type) 也是可变的。

尽管 AttributeConverter types 可以是 mutable 并且 dirty checking, deep copying and second-level caching 都可以正常使用。但将它们当做 immutable 更高效。因此应该尽可能使用 immutable types

package org.example.demo.hibernate;

import javax.persistence.AttributeConverter;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Persistence;

public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager em = factory.createEntityManager();

        Account account = new Account();
        account.balance = new Money(100);

        em.getTransaction().begin();
        em.persist(account);
        em.getTransaction().commit();

        em.clear();

        Account a = em.find(Account.class, account.id);
        // 100
        System.out.println(a.balance.getCents());

        em.getTransaction().begin();
        a.balance.setCents(200);
        em.getTransaction().commit();

        em.close();
        factory.close();
    }

    public static class Money {
        private long cents;

        public Money(long cents) {
            this.cents = cents;
        }

        public long getCents() {
            return cents;
        }

        public void setCents(long cents) {
            this.cents = cents;
        }
    }

    public static class MoneyConverter implements AttributeConverter<Money, Long> {
        @Override
        public Long convertToDatabaseColumn(Money attribute) {
            return attribute == null ? null : attribute.getCents();
        }

        @Override
        public Money convertToEntityAttribute(Long dbData) {
            return dbData == null ? null : new Money(dbData);
        }
    }

    @Entity(name = "Account")
    public static class Account {
        @Id
        @GeneratedValue
        Long id;
        String owner;
        @Convert(converter = MoneyConverter.class)
        Money balance;
    }
}

生成的 SQL

drop table if exists Account cascade
drop sequence if exists hibernate_sequence

create sequence hibernate_sequence start 1 increment 1
create table Account (id int8 not null, balance int8, owner varchar(255), primary key (id))
select nextval ('hibernate_sequence')
insert into Account (balance, owner, id) values (100, NULL, 1)
update Account set balance=100, owner=NULL where id=1

select test_accou0_.id as id1_0_0_, test_accou0_.balance as balance2_0_0_, test_accou0_.owner as owner3_0_0_ from Account test_accou0_ where test_accou0_.id=1
update Account set balance=200, owner=NULL where id=1

results matching ""

    No results matching ""