├── .gitignore ├── PostgresJSONB ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── thoughts │ │ │ └── on │ │ │ └── java │ │ │ └── model │ │ │ ├── MyEntity.java │ │ │ ├── MyJson.java │ │ │ ├── MyJsonType.java │ │ │ ├── MyPostgreSQL94Dialect.java │ │ │ └── package-info.java │ └── resources │ │ └── META-INF │ │ └── persistence.xml │ └── test │ ├── java │ └── org │ │ └── thoughts │ │ └── on │ │ └── java │ │ └── date │ │ └── TestJsonbSupport.java │ └── resources │ ├── data.sql │ └── log4j.properties └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | dependency-reduced-pom.xml 8 | buildNumber.properties 9 | .mvn/timing.properties 10 | -------------------------------------------------------------------------------- /PostgresJSONB/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | org.thoughts.on.java 6 | PostgresJSONB 7 | 1.0.0-SNAPSHOT 8 | jar 9 | 10 | 11 | 1.8 12 | 1.8 13 | UTF-8 14 | 5.1.0.Final 15 | 16 | 17 | 18 | 19 | org.hibernate 20 | hibernate-core 21 | ${hibernate.version} 22 | 23 | 24 | org.hibernate 25 | hibernate-entitymanager 26 | ${hibernate.version} 27 | 28 | 29 | org.hibernate.javax.persistence 30 | hibernate-jpa-2.1-api 31 | 1.0.0.Final 32 | 33 | 34 | junit 35 | junit 36 | 4.11 37 | test 38 | 39 | 40 | org.postgresql 41 | postgresql 42 | 9.4.1208 43 | test 44 | 45 | 46 | log4j 47 | log4j 48 | 1.2.17 49 | test 50 | 51 | 52 | org.slf4j 53 | slf4j-log4j12 54 | 1.7.16 55 | test 56 | 57 | 58 | com.fasterxml.jackson.core 59 | jackson-databind 60 | 2.7.4 61 | 62 | 63 | -------------------------------------------------------------------------------- /PostgresJSONB/src/main/java/org/thoughts/on/java/model/MyEntity.java: -------------------------------------------------------------------------------- 1 | package org.thoughts.on.java.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | import javax.persistence.CascadeType; 8 | import javax.persistence.Column; 9 | import javax.persistence.Entity; 10 | import javax.persistence.GeneratedValue; 11 | import javax.persistence.GenerationType; 12 | import javax.persistence.Id; 13 | import javax.persistence.JoinColumn; 14 | import javax.persistence.JoinTable; 15 | import javax.persistence.ManyToMany; 16 | import javax.persistence.OneToMany; 17 | import javax.persistence.Version; 18 | 19 | import org.hibernate.annotations.Type; 20 | 21 | @Entity 22 | public class MyEntity { 23 | 24 | @Id 25 | @GeneratedValue(strategy = GenerationType.AUTO) 26 | @Column(name = "id", updatable = false, nullable = false) 27 | private Long id; 28 | 29 | @Column 30 | @Type(type = "MyJsonType") 31 | private MyJson jsonProperty; 32 | 33 | public Long getId() { 34 | return this.id; 35 | } 36 | 37 | public void setId(final Long id) { 38 | this.id = id; 39 | } 40 | 41 | public MyJson getJsonProperty() { 42 | return jsonProperty; 43 | } 44 | 45 | public void setJsonProperty(MyJson jsonProperty) { 46 | this.jsonProperty = jsonProperty; 47 | } 48 | 49 | @Override 50 | public boolean equals(Object obj) { 51 | if (this == obj) { 52 | return true; 53 | } 54 | if (!(obj instanceof MyEntity)) { 55 | return false; 56 | } 57 | MyEntity other = (MyEntity) obj; 58 | if (id != null) { 59 | if (!id.equals(other.id)) { 60 | return false; 61 | } 62 | } 63 | return true; 64 | } 65 | 66 | @Override 67 | public int hashCode() { 68 | final int prime = 31; 69 | int result = 1; 70 | result = prime * result + ((id == null) ? 0 : id.hashCode()); 71 | return result; 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | String result = getClass().getSimpleName() + " "; 77 | if (jsonProperty != null) 78 | result += "jsonProperty: " + jsonProperty.toString(); 79 | return result; 80 | } 81 | } -------------------------------------------------------------------------------- /PostgresJSONB/src/main/java/org/thoughts/on/java/model/MyJson.java: -------------------------------------------------------------------------------- 1 | package org.thoughts.on.java.model; 2 | 3 | import java.io.Serializable; 4 | 5 | public class MyJson implements Serializable { 6 | 7 | private String stringProp; 8 | 9 | private Long longProp; 10 | 11 | public String getStringProp() { 12 | return stringProp; 13 | } 14 | 15 | public void setStringProp(String stringProp) { 16 | this.stringProp = stringProp; 17 | } 18 | 19 | public Long getLongProp() { 20 | return longProp; 21 | } 22 | 23 | public void setLongProp(Long longProp) { 24 | this.longProp = longProp; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /PostgresJSONB/src/main/java/org/thoughts/on/java/model/MyJsonType.java: -------------------------------------------------------------------------------- 1 | package org.thoughts.on.java.model; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.ObjectInputStream; 7 | import java.io.ObjectOutputStream; 8 | import java.io.Serializable; 9 | import java.io.StringWriter; 10 | import java.sql.PreparedStatement; 11 | import java.sql.ResultSet; 12 | import java.sql.SQLException; 13 | import java.sql.Types; 14 | 15 | import org.hibernate.HibernateException; 16 | import org.hibernate.engine.spi.SessionImplementor; 17 | import org.hibernate.usertype.UserType; 18 | 19 | import com.fasterxml.jackson.databind.ObjectMapper; 20 | 21 | public class MyJsonType implements UserType { 22 | 23 | @Override 24 | public int[] sqlTypes() { 25 | return new int[]{Types.JAVA_OBJECT}; 26 | } 27 | 28 | @Override 29 | public Class returnedClass() { 30 | return MyJson.class; 31 | } 32 | 33 | @Override 34 | public Object nullSafeGet(final ResultSet rs, final String[] names, final SessionImplementor session, 35 | final Object owner) throws HibernateException, SQLException { 36 | final String cellContent = rs.getString(names[0]); 37 | if (cellContent == null) { 38 | return null; 39 | } 40 | try { 41 | final ObjectMapper mapper = new ObjectMapper(); 42 | return mapper.readValue(cellContent.getBytes("UTF-8"), returnedClass()); 43 | } catch (final Exception ex) { 44 | throw new RuntimeException("Failed to convert String to Invoice: " + ex.getMessage(), ex); 45 | } 46 | } 47 | 48 | @Override 49 | public void nullSafeSet(final PreparedStatement ps, final Object value, final int idx, 50 | final SessionImplementor session) throws HibernateException, SQLException { 51 | if (value == null) { 52 | ps.setNull(idx, Types.OTHER); 53 | return; 54 | } 55 | try { 56 | final ObjectMapper mapper = new ObjectMapper(); 57 | final StringWriter w = new StringWriter(); 58 | mapper.writeValue(w, value); 59 | w.flush(); 60 | ps.setObject(idx, w.toString(), Types.OTHER); 61 | } catch (final Exception ex) { 62 | throw new RuntimeException("Failed to convert Invoice to String: " + ex.getMessage(), ex); 63 | } 64 | } 65 | 66 | @Override 67 | public Object deepCopy(final Object value) throws HibernateException { 68 | try { 69 | // use serialization to create a deep copy 70 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 71 | ObjectOutputStream oos = new ObjectOutputStream(bos); 72 | oos.writeObject(value); 73 | oos.flush(); 74 | oos.close(); 75 | bos.close(); 76 | 77 | ByteArrayInputStream bais = new ByteArrayInputStream(bos.toByteArray()); 78 | return new ObjectInputStream(bais).readObject(); 79 | } catch (ClassNotFoundException | IOException ex) { 80 | throw new HibernateException(ex); 81 | } 82 | } 83 | 84 | @Override 85 | public boolean isMutable() { 86 | return true; 87 | } 88 | 89 | @Override 90 | public Serializable disassemble(final Object value) throws HibernateException { 91 | return (Serializable) this.deepCopy(value); 92 | } 93 | 94 | @Override 95 | public Object assemble(final Serializable cached, final Object owner) throws HibernateException { 96 | return this.deepCopy(cached); 97 | } 98 | 99 | @Override 100 | public Object replace(final Object original, final Object target, final Object owner) throws HibernateException { 101 | return this.deepCopy(original); 102 | } 103 | 104 | @Override 105 | public boolean equals(final Object obj1, final Object obj2) throws HibernateException { 106 | if (obj1 == null) { 107 | return obj2 == null; 108 | } 109 | return obj1.equals(obj2); 110 | } 111 | 112 | @Override 113 | public int hashCode(final Object obj) throws HibernateException { 114 | return obj.hashCode(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /PostgresJSONB/src/main/java/org/thoughts/on/java/model/MyPostgreSQL94Dialect.java: -------------------------------------------------------------------------------- 1 | package org.thoughts.on.java.model; 2 | 3 | import java.sql.Types; 4 | 5 | import org.hibernate.dialect.PostgreSQL94Dialect; 6 | 7 | public class MyPostgreSQL94Dialect extends PostgreSQL94Dialect { 8 | 9 | public MyPostgreSQL94Dialect() { 10 | this.registerColumnType(Types.JAVA_OBJECT, "jsonb"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /PostgresJSONB/src/main/java/org/thoughts/on/java/model/package-info.java: -------------------------------------------------------------------------------- 1 | @org.hibernate.annotations.TypeDef(name = "MyJsonType", typeClass = MyJsonType.class) 2 | 3 | package org.thoughts.on.java.model; -------------------------------------------------------------------------------- /PostgresJSONB/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Thougths on Java 5 | org.hibernate.jpa.HibernatePersistenceProvider 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /PostgresJSONB/src/test/java/org/thoughts/on/java/date/TestJsonbSupport.java: -------------------------------------------------------------------------------- 1 | package org.thoughts.on.java.date; 2 | 3 | import javax.persistence.EntityManager; 4 | import javax.persistence.EntityManagerFactory; 5 | import javax.persistence.Persistence; 6 | 7 | import org.apache.log4j.Logger; 8 | import org.junit.After; 9 | import org.junit.Assert; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | import org.thoughts.on.java.model.MyEntity; 13 | import org.thoughts.on.java.model.MyJson; 14 | 15 | import com.fasterxml.jackson.core.JsonProcessingException; 16 | import com.fasterxml.jackson.databind.ObjectMapper; 17 | 18 | public class TestJsonbSupport { 19 | 20 | Logger log = Logger.getLogger(this.getClass().getName()); 21 | 22 | private EntityManagerFactory emf; 23 | 24 | @Before 25 | public void init() { 26 | emf = Persistence.createEntityManagerFactory("my-persistence-unit"); 27 | } 28 | 29 | @After 30 | public void close() { 31 | emf.close(); 32 | } 33 | 34 | @Test 35 | public void testJsonMapping() throws JsonProcessingException { 36 | log.info("... testJsonMapping ..."); 37 | 38 | MyJson j = new MyJson(); 39 | j.setLongProp(123L); 40 | j.setStringProp("abc"); 41 | 42 | ObjectMapper mapper = new ObjectMapper(); 43 | System.out.println(mapper.writeValueAsString(j)); 44 | } 45 | 46 | @Test 47 | public void testCreateJsonbEntity() { 48 | log.info("... testCreateJsonbEntity ..."); 49 | 50 | EntityManager em = emf.createEntityManager(); 51 | em.getTransaction().begin(); 52 | 53 | MyJson j = new MyJson(); 54 | j.setLongProp(123L); 55 | j.setStringProp("abc"); 56 | 57 | MyEntity e = new MyEntity(); 58 | e.setJsonProperty(j); 59 | em.persist(e); 60 | 61 | em.getTransaction().commit(); 62 | em.close(); 63 | } 64 | 65 | @Test 66 | public void testUpdateJsonbEntity() { 67 | log.info("... testUpdateJsonbEntity ..."); 68 | 69 | EntityManager em = emf.createEntityManager(); 70 | em.getTransaction().begin(); 71 | 72 | MyEntity e = em.find(MyEntity.class, 10000L); 73 | 74 | e.getJsonProperty().setStringProp("changed"); 75 | e.getJsonProperty().setLongProp(789L); 76 | 77 | em.getTransaction().commit(); 78 | em.close(); 79 | } 80 | 81 | @Test 82 | public void testSelectJsonbEntity() { 83 | log.info("... testSelectJsonbEntity ..."); 84 | 85 | EntityManager em = emf.createEntityManager(); 86 | em.getTransaction().begin(); 87 | 88 | MyEntity e = (MyEntity) em.createNativeQuery("SELECT * FROM myentity e WHERE e.jsonproperty->'longProp' = '456'", MyEntity.class).getSingleResult(); 89 | 90 | Assert.assertNotNull(e.getJsonProperty()); 91 | System.out.println("JSON: stringProp = "+e.getJsonProperty().getStringProp()+" longProp = "+e.getJsonProperty().getLongProp()); 92 | 93 | em.getTransaction().commit(); 94 | em.close(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /PostgresJSONB/src/test/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO myentity (id, jsonproperty) VALUES (10000, '{"longProp": 456, "stringProp": "xyz"}'); -------------------------------------------------------------------------------- /PostgresJSONB/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 2 | log4j.appender.stdout.Target=System.out 3 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] - %m%n 5 | 6 | # 7 | # Development 8 | # 9 | log4j.rootLogger=info, stdout 10 | # basic log level for all messages 11 | log4j.logger.org.hibernate=info 12 | 13 | # SQL statements and parameters 14 | log4j.logger.org.hibernate.SQL=debug 15 | #log4j.logger.org.hibernate.type.descriptor.sql=trace 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HibernateJSONBSupport --------------------------------------------------------------------------------