Skip to content

Commit

Permalink
enhancement(jpa): add json type (#2211)
Browse files Browse the repository at this point in the history
* add json type

* remove package-info

* add integrate test
  • Loading branch information
ungreat committed Apr 22, 2024
1 parent aa7f475 commit 4f55c3c
Show file tree
Hide file tree
Showing 4 changed files with 288 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2023 OceanBase.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.oceanbase.odc.metadb.jpa;

import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;

import com.oceanbase.odc.config.jpa.type.JsonType;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@TypeDefs({@TypeDef(name = "Json", typeClass = JsonType.class)})
@Data
@Table(name = "json_example_entity")
class JsonExampleEntity {

@Id
private Long id;

@Type(type = "Json")
@Column(name = "json_example")
private JsonExampleNested jsonExample;

@Type(type = "Json")
@Column(name = "json_array")
private List<List<String>> jsonArray;


@Data
@NoArgsConstructor
@AllArgsConstructor
static class JsonExampleNested {
private String nestedField1;
private int nestedField2;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2023 OceanBase.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.oceanbase.odc.metadb.jpa;

import com.oceanbase.odc.config.jpa.OdcJpaRepository;

public interface JsonExampleRepository extends OdcJpaRepository<JsonExampleEntity, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2023 OceanBase.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.oceanbase.odc.metadb.jpa;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import com.oceanbase.odc.ServiceTestEnv;
import com.oceanbase.odc.metadb.jpa.JsonExampleEntity.JsonExampleNested;

public class JsonTypeTest extends ServiceTestEnv {

@Autowired
JsonExampleRepository jsonExampleRepository;


/**
* CREATE TABLE IF NOT EXISTS `json_example_entity` ( `id` INT(11) NOT NULL PRIMARY KEY,
* `json_example` TEXT DEFAULT NULL, `json_array` TEXT NULL DEFAULT NULL, );
*/
@Before
public void init() {
jsonExampleRepository.getJdbcTemplate().execute("CREATE TABLE IF NOT EXISTS json_example_entity (\n"
+ " id INT NOT NULL PRIMARY KEY,\n"
+ " json_example TEXT DEFAULT NULL,\n"
+ " json_array TEXT NULL DEFAULT NULL\n"
+ " );");
JsonExampleEntity entity = new JsonExampleEntity();
entity.setId(1L);
entity.setJsonExample(new JsonExampleNested("111", 222));
ArrayList<List<String>> lists = new ArrayList<>();
lists.add(Arrays.asList("123", "456"));
entity.setJsonArray(lists);
jsonExampleRepository.save(entity);
}

@Test
public void test_jsonExampleEntity_deserialization() {
Optional<JsonExampleEntity> byId = jsonExampleRepository.findById(1L);
Assert.assertTrue(byId.isPresent());
JsonExampleNested jsonExampleEntity = byId.get().getJsonExample();
Assert.assertEquals("111", jsonExampleEntity.getNestedField1());
Assert.assertEquals(222, jsonExampleEntity.getNestedField2());
List<List<String>> jsonArray = byId.get().getJsonArray();
Assert.assertEquals(Arrays.asList("123", "456"), jsonArray.get(0));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Copyright (c) 2023 OceanBase.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.oceanbase.odc.config.jpa.type;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Objects;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.annotations.common.reflection.java.JavaXMember;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.DynamicParameterizedType;
import org.hibernate.usertype.UserType;

import com.fasterxml.jackson.databind.JavaType;
import com.oceanbase.odc.common.json.JsonUtils;

public class JsonType implements UserType, DynamicParameterizedType {

private Class<?> returnedClass;

private JavaType targetJavaType;

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

@Override
public Class<?> returnedClass() {
return returnedClass;
}

@Override
public boolean equals(Object x, Object y) throws HibernateException {
return Objects.equals(x, y);
}

@Override
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}

@Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
throws HibernateException,
SQLException {
String value = rs.getString(names[0]);
return fromJson(value);
}

@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, Types.CLOB);
return;
}
st.setString(index, toJson(value));
}

private Object fromJson(String value) {
if (value == null) {
return null;
}
return JsonUtils.fromJson(value, targetJavaType);
}

private String toJson(Object value) {
if (value == null) {
return null;
}
return JsonUtils.toJson(value);
}

@Override
public Object deepCopy(Object value) throws HibernateException {
return fromJson(toJson(value));
}

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

@Override
public Serializable disassemble(Object value) throws HibernateException {
throw new UnsupportedOperationException("disassemble is not supported");
}

@Override
public Object assemble(Serializable cached, Object owner) throws HibernateException {
throw new UnsupportedOperationException("assemble is not supported");
}

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

@Override
public void setParameterValues(Properties parameters) {
Object xProperty = parameters.get(DynamicParameterizedType.XPROPERTY);
Field annotatedField = (Field) getAnnotatedElement(xProperty);

returnedClass = annotatedField.getType();
Type type = annotatedField.getGenericType();
targetJavaType = JsonUtils.constructType(type);
}

private Object getAnnotatedElement(Object target) {
try {
Field field = JavaXMember.class.getSuperclass().getDeclaredField("annotatedElement");
field.setAccessible(true);
return field.get(target);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new AssertionError(e);
}
}

}

0 comments on commit 4f55c3c

Please sign in to comment.