diff --git a/build.gradle b/build.gradle index 749c719..21842d8 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,9 @@ java { dependencies { implementation 'org.hibernate:hibernate-core:5.5.6.Final' runtimeOnly 'org.postgresql:postgresql:42.2.23.jre7' + implementation 'com.vladmihalcea:hibernate-types-52:2.12.1' + implementation 'org.slf4j:slf4j-api:1.7.32' + compileOnly 'org.projectlombok:lombok:1.18.20' annotationProcessor 'org.projectlombok:lombok:1.18.20' diff --git a/src/main/java/com/dmdev/HibernateRunner.java b/src/main/java/com/dmdev/HibernateRunner.java index 305d1d2..f0750d4 100644 --- a/src/main/java/com/dmdev/HibernateRunner.java +++ b/src/main/java/com/dmdev/HibernateRunner.java @@ -1,12 +1,13 @@ package com.dmdev; +import com.dmdev.converter.BirthdayConverter; import com.dmdev.entity.User; +import com.vladmihalcea.hibernate.type.json.JsonBinaryType; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import java.sql.SQLException; -import java.time.LocalDate; public class HibernateRunner { @@ -21,20 +22,29 @@ public static void main(String[] args) throws SQLException { Configuration configuration = new Configuration(); // configuration.setPhysicalNamingStrategy(new CamelCaseToUnderscoresNamingStrategy()); // configuration.addAnnotatedClass(User.class); + configuration.addAttributeConverter(new BirthdayConverter()); + configuration.registerTypeOverride(new JsonBinaryType()); configuration.configure(); try (SessionFactory sessionFactory = configuration.buildSessionFactory(); Session session = sessionFactory.openSession()) { session.beginTransaction(); - User user = User.builder() - .username("ivan@gmail.com") - .firstname("Ivan") - .lastname("Ivanov") - .birthDate(LocalDate.of(2000, 1, 19)) - .age(20) - .build(); - session.save(user); +// User user = User.builder() +// .username("ivan9@gmail.com") +// .firstname("Ivan") +// .lastname("Ivanov") +// .info(""" +// { +// "name": "Ivan", +// "id": 25 +// } +// """) +// .birthDate(new Birthday(LocalDate.of(2000, 1, 19))) +// .role(Role.ADMIN) +// .build(); +// session.delete(user); + User user = session.get(User.class, "ivan@gmail.com"); session.getTransaction().commit(); } diff --git a/src/main/java/com/dmdev/converter/BirthdayConverter.java b/src/main/java/com/dmdev/converter/BirthdayConverter.java new file mode 100644 index 0000000..43e13d6 --- /dev/null +++ b/src/main/java/com/dmdev/converter/BirthdayConverter.java @@ -0,0 +1,28 @@ +package com.dmdev.converter; + +import com.dmdev.entity.Birthday; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; +import java.sql.Date; +import java.util.Optional; + +@Converter(autoApply = true) +public class BirthdayConverter implements AttributeConverter { + + @Override + public Date convertToDatabaseColumn(Birthday attribute) { + return Optional.ofNullable(attribute) + .map(Birthday::birthDate) + .map(Date::valueOf) + .orElse(null); + } + + @Override + public Birthday convertToEntityAttribute(Date dbData) { + return Optional.ofNullable(dbData) + .map(Date::toLocalDate) + .map(Birthday::new) + .orElse(null); + } +} diff --git a/src/main/java/com/dmdev/entity/Birthday.java b/src/main/java/com/dmdev/entity/Birthday.java new file mode 100644 index 0000000..ebe1058 --- /dev/null +++ b/src/main/java/com/dmdev/entity/Birthday.java @@ -0,0 +1,11 @@ +package com.dmdev.entity; + +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; + +public record Birthday(LocalDate birthDate) { + + public long getAge() { + return ChronoUnit.YEARS.between(birthDate, LocalDate.now()); + } +} diff --git a/src/main/java/com/dmdev/entity/Role.java b/src/main/java/com/dmdev/entity/Role.java new file mode 100644 index 0000000..6550f5d --- /dev/null +++ b/src/main/java/com/dmdev/entity/Role.java @@ -0,0 +1,6 @@ +package com.dmdev.entity; + +public enum Role { + ADMIN, + USER +} diff --git a/src/main/java/com/dmdev/entity/User.java b/src/main/java/com/dmdev/entity/User.java index 12893bb..803beec 100644 --- a/src/main/java/com/dmdev/entity/User.java +++ b/src/main/java/com/dmdev/entity/User.java @@ -1,15 +1,19 @@ package com.dmdev.entity; +import com.vladmihalcea.hibernate.type.json.JsonBinaryType; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.Table; -import java.time.LocalDate; @Data @NoArgsConstructor @@ -17,13 +21,21 @@ @Builder @Entity @Table(name = "users", schema = "public") +@TypeDef(name = "dmdev", typeClass = JsonBinaryType.class) public class User { @Id private String username; private String firstname; private String lastname; + +// @Convert(converter = BirthdayConverter.class) @Column(name = "birth_date") - private LocalDate birthDate; - private Integer age; + private Birthday birthDate; + + @Type(type = "dmdev") + private String info; + + @Enumerated(EnumType.STRING) + private Role role; } diff --git a/src/main/java/com/dmdev/type/JsonType.java b/src/main/java/com/dmdev/type/JsonType.java new file mode 100644 index 0000000..93e4b5b --- /dev/null +++ b/src/main/java/com/dmdev/type/JsonType.java @@ -0,0 +1,68 @@ +package com.dmdev.type; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.usertype.UserType; + +import java.io.Serializable; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +public class JsonType implements UserType { + + @Override + public int[] sqlTypes() { + return new int[0]; + } + + @Override + public Class returnedClass() { + return null; + } + + @Override + public boolean equals(Object x, Object y) throws HibernateException { + return false; + } + + @Override + public int hashCode(Object x) throws HibernateException { + return 0; + } + + @Override + public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { + return null; + } + + @Override + public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException { + + } + + @Override + public Object deepCopy(Object value) throws HibernateException { + return null; + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public Serializable disassemble(Object value) throws HibernateException { + return null; + } + + @Override + public Object assemble(Serializable cached, Object owner) throws HibernateException { + return null; + } + + @Override + public Object replace(Object original, Object target, Object owner) throws HibernateException { + return null; + } +} diff --git a/src/test/java/com/dmdev/HibernateRunnerTest.java b/src/test/java/com/dmdev/HibernateRunnerTest.java new file mode 100644 index 0000000..809032a --- /dev/null +++ b/src/test/java/com/dmdev/HibernateRunnerTest.java @@ -0,0 +1,87 @@ +package com.dmdev; + +import com.dmdev.entity.User; +import org.junit.jupiter.api.Test; + +import javax.persistence.Column; +import javax.persistence.Table; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; + +import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.joining; + +class HibernateRunnerTest { + + @Test + void checkGetReflectionApi() throws SQLException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { + PreparedStatement preparedStatement = null; + ResultSet resultSet = preparedStatement.executeQuery(); + resultSet.getString("username"); + resultSet.getString("lastname"); + resultSet.getString("lastname"); + + Class clazz = User.class; + + Constructor constructor = clazz.getConstructor(); + User user = constructor.newInstance(); + Field usernameField = clazz.getDeclaredField("username"); + usernameField.setAccessible(true); + usernameField.set(user, resultSet.getString("username")); + } + + @Test + void checkReflectionApi() throws SQLException, IllegalAccessException { + User user = User.builder() + .build(); + + String sql = """ + insert + into + %s + (%s) + values + (%s) + """; + String tableName = ofNullable(user.getClass().getAnnotation(Table.class)) + .map(tableAnnotation -> tableAnnotation.schema() + "." + tableAnnotation.name()) + .orElse(user.getClass().getName()); + + Field[] declaredFields = user.getClass().getDeclaredFields(); + + String columnNames = Arrays.stream(declaredFields) + .map(field -> ofNullable(field.getAnnotation(Column.class)) + .map(Column::name) + .orElse(field.getName())) + .collect(joining(", ")); + + String columnValues = Arrays.stream(declaredFields) + .map(field -> "?") + .collect(joining(", ")); + + System.out.println(sql.formatted(tableName, columnNames, columnValues)); + + Connection connection = null; + PreparedStatement preparedStatement = connection.prepareStatement(sql.formatted(tableName, columnNames, columnValues)); + for (Field declaredField : declaredFields) { + declaredField.setAccessible(true); + preparedStatement.setObject(1, declaredField.get(user)); + } + } + + + + + + + + + + +} \ No newline at end of file