1 package com.vladmihalcea.hibernate.type.json.internal;
2
3 import com.vladmihalcea.hibernate.type.util.ParameterTypeUtils;
4 import com.vladmihalcea.hibernate.util.StringUtils;
5 import org.hibernate.dialect.*;
6 import org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver;
7 import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter;
8 import org.hibernate.type.descriptor.ValueBinder;
9 import org.hibernate.type.descriptor.WrapperOptions;
10 import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
11 import org.hibernate.type.descriptor.sql.BasicBinder;
12 import org.hibernate.usertype.DynamicParameterizedType;
13 import org.hibernate.usertype.ParameterizedType;
14
15 import java.sql.*;
16 import java.util.Properties;
17
18 /**
19  * @author Vlad Mihalcea
20  */

21 public class JsonSqlTypeDescriptor extends AbstractJsonSqlTypeDescriptor implements ParameterizedType {
22
23     private volatile Dialect dialect;
24     private volatile AbstractJsonSqlTypeDescriptor sqlTypeDescriptor;
25
26     private volatile Properties properties;
27
28     public JsonSqlTypeDescriptor() {
29     }
30
31     public JsonSqlTypeDescriptor(Properties properties) {
32         this.properties = properties;
33     }
34
35     @Override
36     public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
37         return new BasicBinder<X>(javaTypeDescriptor, this) {
38             @Override
39             protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
40                 sqlTypeDescriptor(st.getConnection()).getBinder(javaTypeDescriptor).bind(
41                     st, value, index, options
42                 );
43             }
44
45             @Override
46             protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
47                 throws SQLException {
48                 sqlTypeDescriptor(st.getConnection()).getBinder(javaTypeDescriptor).bind(
49                     st, value, name, options
50                 );
51             }
52         };
53     }
54
55     @Override
56     protected Object extractJson(ResultSet rs, String name) throws SQLException {
57         return sqlTypeDescriptor(rs.getStatement().getConnection()).extractJson(rs, name);
58     }
59
60     @Override
61     protected Object extractJson(CallableStatement statement, int index) throws SQLException {
62         return sqlTypeDescriptor(statement.getConnection()).extractJson(statement, index);
63     }
64
65     @Override
66     protected Object extractJson(CallableStatement statement, String name) throws SQLException {
67         return sqlTypeDescriptor(statement.getConnection()).extractJson(statement, name);
68     }
69
70     private AbstractJsonSqlTypeDescriptor sqlTypeDescriptor(Connection connection) {
71         if (sqlTypeDescriptor == null) {
72             sqlTypeDescriptor = resolveSqlTypeDescriptor(connection);
73         }
74         return sqlTypeDescriptor;
75     }
76
77     private AbstractJsonSqlTypeDescriptor resolveSqlTypeDescriptor(Connection connection) {
78         try {
79             StandardDialectResolver dialectResolver = new StandardDialectResolver();
80             DatabaseMetaDataDialectResolutionInfoAdapter metaDataInfo = new DatabaseMetaDataDialectResolutionInfoAdapter(connection.getMetaData());
81             dialect = dialectResolver.resolveDialect(metaDataInfo);
82             if (dialect instanceof PostgreSQL81Dialect) {
83                 return JsonBinarySqlTypeDescriptor.INSTANCE;
84             } else if (dialect instanceof H2Dialect) {
85                 return JsonBytesSqlTypeDescriptor.INSTANCE;
86             } else if (dialect instanceof Oracle8iDialect) {
87                 if (properties != null) {
88                     DynamicParameterizedType.ParameterType parameterType = ParameterTypeUtils.resolve(properties);
89                     if (parameterType != null) {
90                         String columnType = ParameterTypeUtils.getColumnType(parameterType);
91                         if (!StringUtils.isBlank(columnType)) {
92                             switch (columnType) {
93                                 case "json":
94                                     return JsonBytesSqlTypeDescriptor.of(Database.ORACLE);
95                                 case "blob":
96                                 case "clob":
97                                     return JsonBlobSqlTypeDescriptor.INSTANCE;
98                                 case "varchar2":
99                                 case "nvarchar2":
100                                     return JsonStringSqlTypeDescriptor.INSTANCE;
101                             }
102                         }
103                     }
104                 }
105                 if (metaDataInfo.getDatabaseMajorVersion() >= 21) {
106                     return JsonBytesSqlTypeDescriptor.of(Database.ORACLE);
107                 }
108             }
109             return JsonStringSqlTypeDescriptor.INSTANCE;
110         } catch (SQLException e) {
111             throw new IllegalStateException(e);
112         }
113     }
114
115     @Override
116     public int getSqlType() {
117         return sqlTypeDescriptor != null ?
118             sqlTypeDescriptor.getSqlType() :
119             super.getSqlType();
120     }
121
122     @Override
123     public void setParameterValues(Properties parameters) {
124         if (properties == null) {
125             properties = parameters;
126         } else {
127             properties.putAll(parameters);
128         }
129     }
130 }
131