Parse string as json array from Postgre - json

I have a table in PostgreSQL with 2 columns - Id and coord.
Column "coord" - geo coordinates stored as a string in JSON format.
Example:
[{"lat":49.09693425316379,"lng":33.61747393628419},{"lat":49.11835977646441,"lng":33.638456496907},{"lat":49.12103137811804,"lng":33.63866144845382},{"lat":49.09694682809236,"lng":33.61746879914138},{"lat":49.08920750204137,"lng":33.61734796797724},{"lat":49.07643862058337,"lng":33.61246117651179}]
How to send such string as JSON Array of objects(POST request).
Entity without getters and setters
public class Lepcoord implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 30)
#Column(name = "tplnr")
private String tplnr;
#Size(max = 2147483647)
#Column(name = "coord")
private String coord;
Controller
#POST
#RequestMapping(value= "/lep/{voltage}", method = RequestMethod.POST, headers = "Accept=application/json")
#ResponseBody
public ResponseEntity<List<Lepcoord>> lep (#PathVariable String voltage)
{
return new ResponseEntity<>(gisDaoService.lep(voltage), HttpStatus.OK);
}
And service
#Transactional(readOnly = true)
public List <Lepcoord> lep (String voltage) {
Query query = this.em.createQuery(
" From Lepcoord ");
List <Lepcoord> rez = null;
try {
rez = (List<Lepcoord>) query.getResultList();
} catch (PersistenceException r) {
return null;
}
return rez;
}
Hibernate cant handle json type If i storeing coord as json in Postgre. May be someone knows easier way. Not to write own classes to work with Postgres json type

You are using Hibernate so it is good to use a custom UserType which knows how to handle json.
create a hibernate usertype
public class GeoJsonType implements UserType
{
protected static final int[] SQL_TYPES = { java.sql.Types.VARCHAR };
#Override
public int[] sqlTypes()
{
return SQL_TYPES;
}
#Override
public Class returnedClass()
{
return GeoEntity.class;
}
#Override
public boolean equals(Object x, Object y) throws HibernateException
{
if (x == y)
{
return true;
}
else if (x == null || y == null)
{
return false;
}
else
{
return x.equals(y);
}
}
#Override
public int hashCode(Object x) throws HibernateException
{
return x.hashCode();
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException
{
// if (rs.wasNull())
// {
// return null;
// }
//this is your json stored in db
String rsArr = rs.getString(names[0]);
if (rsArr == null)
return null;
GeoEntity detailAttr = JSON.toObject(rsArr, GeoEntity.class, null);
return detailAttr;
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException
{
if (value == null)
{
st.setNull(index, SQL_TYPES[0]);
}
else
{
//when stroing object into db convert it to json
GeoEntity castObject = (GeoEntity) value;
String json = JSON.toJson(castObject);
st.setString(index, json);
}
}
#Override
public Object deepCopy(Object value) throws HibernateException
{
return value;
}
#Override
public boolean isMutable()
{
return true;
}
#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 original;
}
}
Your Entity.java
#Type(type = "FQN to your GeoJsonType")
#Column(name = "geo")
public GeoEntity getGeo()
{
return geo;
}

Postgres supports the json_to_array function that should be of help here. Take a look at the documentation here.
Alternatively, there is this answer on SO: How to turn a json array into rows in postgres that could point you in the right direction.

Related

Convert the List<Long> of java bean to the json of mysql in Spring data JPA

I have tried to use #Convert by spring-data-jpa, but I get an error, this is my code:
entity
#Convert(converter = ListExtendConverterJson.class)
private List<Long> receivers;
ListExtendConverterJson implements AttributeConverter<List<Long>, String>:
#Override
public String convertToDatabaseColumn(List<Long> list) {
String result = JSONArray.toJSONString(list);
return result;
}
#Override
public List<Long> convertToEntityAttribute(String s) {
List<Long> result = JSONArray.parseArray(s, Long.class);
return result;
}
This is the error message:
Caused by: java.lang.ClassCastException: java.lang.Long cannot be cast to java.util.List
at com.qf.posp.pub.config.entity.json.ListExtendConverterJson.convertToDatabaseColumn(ListExtendConverterJson.java:23)
at org.hibernate.type.descriptor.converter.AttributeConverterSqlTypeDescriptorAdapter$1.bind(AttributeConverterSqlTypeDescriptorAdapter.java:78)
... 78 common frames omitted
So what is wrong?
At last, i solved this problem. I change my code like this:
entity:
#Convert(converter = ListExtendConverterJson.class)
private Long[] receivers;
public class ListExtendConverterJson implements AttributeConverter<Long[], String> {
#Override
public String convertToDatabaseColumn(Long[] list) {
String result = JSONArray.toJSONString(list);
return result;
}
#Override
public Long[] convertToEntityAttribute(String s) {
List<Long> list = JSONArray.parseArray(s, Long.class);
Long[] result = new Long[list == null ? 0 :list.size()];
if(!CollectionUtils.isEmpty(list)) {
int i = 0;
for(Long l : list) {
result[i] = l;
i ++;
}
}
return result;
}
Change the List Collection to a Array.By this way, it work normally!

<p:selectOneMenu> <f:selectItems> shows toString() in the itemLabel

When I use f:selectItems the itemLabel doesnt show the property descricao, but show the toString(). I've made some researches, but the problem continues. <f:selectItems> only shows toString() of the model as item label
What am I doing wrong? Any ideas?
I have a class Tipo as follow:
public class Tipo implements Serializable{
/**
*
*/
private static final long serialVersionUID = -763536865855419703L;
// descrição do tipo
private String descricao;
// código do tipo
private Long tipoId;
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException cnse) {
return null;
}
}
public Tipo(Long id) {
this.tipoId = id;
}
public Tipo() {
}
public String getDescricao() {
return descricao;
}
public Long getTipoId() {
return tipoId;
}
public void setDescricao(String umaDesc) {
this.descricao = umaDesc;
}
public void setTipoId(Long id) {
this.tipoId = id;
}
public String toString() {
return " ID=" + this.getTipoId() + ", Descricao=" + this.getDescricao();
}
#Override
public boolean equals(Object other){
return (other != null && getClass() == other.getClass() && tipoId != null)
? tipoId.equals(((Tipo) other).tipoId) : (other == this);
}
#Override
public int hashCode() {
return (tipoId != null)
? (getClass().hashCode() + tipoId.hashCode()) : super.hashCode();
}
}
And a TipoDAOImpl:
public class TipoDAOImpl extends NamedParameterJdbcDaoSupport implements TipoDAO, Serializable {
private static final long serialVersionUID = 8698127647660788120L;
private SimpleJdbcInsert sji;
#Value("#{queries.sql03}")
private String sql03;
#Value("#{queries.sql04}")
private String sql04;
#Override
public List<Tipo> getTodosTipos() throws DAOException {
try {
RowMapper<Tipo> mapper = getRowMapper();
return getJdbcTemplate().query(this.sql03, mapper);
} catch (EmptyResultDataAccessException ex) {
throw new DAOException("Não há registros na tabela de tipos.");
} catch (DataAccessException e) {
throw new DAOException(e.getMessage());
}
}
private RowMapper<Tipo> getRowMapper() {
RowMapper<Tipo> mapper = new RowMapper<Tipo>() {
public Tipo mapRow(ResultSet rs, int rowNum) throws SQLException {
Tipo t = new Tipo();
t.setTipoId(rs.getLong("tipo_id"));
t.setDescricao(rs.getString("descricao"));
return t;
}
};
return mapper;
}
protected SimpleJdbcInsert getSji() {
return sji;
}
protected void setSji(SimpleJdbcInsert sji) {
this.sji = sji;
}
}
ManagedBean:
#ManagedBean
#SessionScoped
public class TipoMB extends ManagedBeanBasico implements Serializable{
private static final long serialVersionUID = 2482494734070978599L;
#ManagedProperty(name = "tipoFacade", value = "#{tipoFacade}")
private TipoFacade tipoFacade;
private List<Tipo> listTipos;
private Tipo tipo;
public List<Tipo> getTodosTipos(){
try {
listTipos = tipoFacade.getTodosTipos();
} catch (DAOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return listTipos;
}
Converter:
#FacesConverter(value="tipoConverter")
public class TipoConverter implements Converter {
#EJB private Tipo tipo;
#EJB private TipoFacade tipoFacade;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value)
throws ConverterException {
try {
return tipoFacade.getTipoPorId(Long.parseLong(value));
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (DAOException e) {
e.printStackTrace();
}
return value;
}
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) throws ConverterException {
if (value == null) {
return "";
}
if (!(value instanceof Tipo)) {
throw new ConverterException("Não é um tipo válido " + value );
}
return ((Tipo) value).getTipoId().toString();
}
form.xhtml:
<h:outputText value="TIPO:"/>
<p:selectOneMenu value="#{publicacaoMB.publicacao.tipo}" converter="tipoConverter">
<f:selectItems value="#{tipoMB.listTipos}" var="tipo"
itemLabel="#{tipo.descricao}" itemValue="#{tipo.tipoId}"/>
</p:selectOneMenu>
I think this is a Primefaces bug. When the itemLabel expression resolves to null (#{myObject.name} => null), Primefaces shows the toString value of the object. That's wrong because the toString method may not have been overridden and that will result in presenting the internals of the application (class name, etc.) to the end user. It happened to me during a presentation and it was quite embarrassing. The value was null because of bad data in the database.
I guess the PF implementor assumed that if itemLabel was null, it's because it was not set and that the intend was indeed to use toString on the whole object. But itemLabel may have been set but resolved to null, in which case Primefaces should show a null value, being it "null", empty string, "!!", etc.
The selectItems tag could have a "null value" property to tell PF what to show in case itemLabel resolves to null.
Look like you got the converter logic wrong
In getAsObject you are not returning 1 object but a whole array. You are supposed to return just one, by mathching one of the tipo attributes. You could use the tipoId as long as it's unique per tipo instance. The hashCode might also do the trick.
In getAsString, you should return the same attribute you're using in getAsObject to identify objects. The descricao doesn't seem right.
In the selectOneMenu component, use the object itself, not it's attribute (itemValue="#{tipo}")

How can I map postgresql json data type using Hibernate?

I am following the example mentioned in the below URL ?
Mapping PostgreSQL JSON column to a Hibernate entity property
But always get the following exception:
Caused by: org.hibernate.MappingException: No Dialect mapping for JDBC type: 2000
at org.hibernate.dialect.TypeNames.get(TypeNames.java:76)
at org.hibernate.dialect.TypeNames.get(TypeNames.java:99)
at org.hibernate.dialect.Dialect.getTypeName(Dialect.java:310)
at org.hibernate.mapping.Column.getSqlType(Column.java:226)
at org.hibernate.mapping.Table.validateColumns(Table.java:369)
at org.hibernate.cfg.Configuration.validateSchema(Configuration.java:1305)
at org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:155)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:512)
I am using TomEE as the Server. and trying to store Json body to a postgresql column. I am trying to map the entity pojos to the postgres datatype structure.
Any idea what could be the issue ? or does any has a better technique to handle such as scenario ? Please point me to that source.
The script used to create the entity table is:
CREATE TABLE historyentity
(
id character varying(255) NOT NULL,
userid character varying(255),
lastchanged timestamp without time zone,
type character varying(255),
history json [],
CONSTRAINT historyentity_pkey PRIMARY KEY (id),
CONSTRAINT historyentity_userid_fkey FOREIGN KEY (userid)
REFERENCES userentity (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE historyentity
OWNER TO postgres;
GRANT ALL ON TABLE historyentity TO postgres;
Entity Pojos look like as follows:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#TypeDefs({ #TypeDef(name = "StringJsonObject", typeClass = StringJsonUserType.class) })
public class HistoryEntity {
#Id
private String id;
private String userid;
private String type;
#Type(type = "StringJsonObject")
private String history;
private Date lastchanged;
}
I am using lombok to define the entity pojos.
Following is the Dialect extended class:
I have tried with both the registered types, Column and Hibenate. But both are not working out.
import org.hibernate.dialect.PostgreSQL82Dialect;
public class JsonPostgreSQLDialect extends PostgreSQL82Dialect
{
#Inject
public JsonPostgreSQLDialect()
{
super();
this.registerColumnType(Types.JAVA_OBJECT, "json");
// this.registerHibernateType(Types.JAVA_OBJECT, "json");
}
}
The following class is being used to define the User Type:
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;
public class StringJsonUserType implements UserType
{
private final int[] sqlTypesSupported = new int[]{ Types.JAVA_OBJECT };
/**
* Return the SQL type codes for the columns mapped by this type. The codes are defined on <tt>java.sql.Types</tt>.
*
* #return int[] the typecodes
* #see java.sql.Types
*/
#Override
public int[] sqlTypes()
{
return sqlTypesSupported;
}
/**
* The class returned by <tt>nullSafeGet()</tt>.
*
* #return Class
*/
#Override
public Class returnedClass()
{
return String.class;
}
/**
* Compare two instances of the class mapped by this type for persistence "equality". Equality of the persistent
* state.
*
* #return boolean
*/
#Override
public boolean equals(Object x, Object y) throws HibernateException
{
if (x == null)
{
return y == null;
}
return x.equals(y);
}
/**
* Get a hashcode for the instance, consistent with persistence "equality"
*/
#Override
public int hashCode(Object x) throws HibernateException
{
return x.hashCode();
}
/**
* Retrieve an instance of the mapped class from a JDBC resultset. Implementors should handle possibility of null
* values.
*
* #param rs a JDBC result set
* #param names the column names
* #param owner the containing entity #return Object
*/
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException
{
if (rs.getString(names[0]) == null)
{
return null;
}
return rs.getString(names[0]);
}
/**
* Write an instance of the mapped class to a prepared statement. Implementors should handle possibility of null
* values. A multi-column type should be written to parameters starting from <tt>index</tt>.
*
* #param st a JDBC prepared statement
* #param value the object to write
* #param index statement parameter index
*/
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
throws HibernateException, SQLException
{
if (value == null)
{
st.setNull(index, Types.OTHER);
return;
}
st.setObject(index, value, Types.OTHER);
}
/**
* Return a deep copy of the persistent state, stopping at entities and at collections. It is not necessary to copy
* immutable objects, or null values, in which case it is safe to simply return the argument.
*
* #param value the object to be cloned, which may be null
* #return Object a copy
*/
#Override
public Object deepCopy(Object value) throws HibernateException
{
return value;
}
/**
* Are objects of this type mutable?
*
* #return boolean
*/
#Override
public boolean isMutable()
{
return true;
}
/**
* Transform the object into its cacheable representation. At the very least this method should perform a deep copy
* if the type is mutable. That may not be enough for some implementations, however; for example, associations must
* be cached as identifier values. (optional operation)
*
* #param value the object to be cached
* #return a cachable representation of the object
*/
#Override
public Serializable disassemble(Object value) throws HibernateException
{
return (String) this.deepCopy(value);
}
/**
* Reconstruct an object from the cacheable representation. At the very least this method should perform a deep copy
* if the type is mutable. (optional operation)
*
* #param cached the object to be cached
* #param owner the owner of the cached object
* #return a reconstructed object from the cachable representation
*/
#Override
public Object assemble(Serializable cached, Object owner) throws HibernateException
{
return this.deepCopy(cached);
}
/**
* During merge, replace the existing (target) value in the entity we are merging to with a new (original) value
* from the detached entity we are merging. For immutable objects, or null values, it is safe to simply return the
* first parameter. For mutable objects, it is safe to return a copy of the first parameter. For objects with
* component values, it might make sense to recursively replace component values.
*
* #param original the value from the detached entity being merged
* #param target the value in the managed entity
* #return the value to be merged
*/
#Override
public Object replace(Object original, Object target, Object owner) throws HibernateException
{
return original;
}
}
This work for me :
Your Entity :
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#TypeDef(name = "json", typeClass = JSONUserType.class, parameters = {
#Parameter(name = JSONUserType.CLASS, value = "java.lang.String")})
public class HistoryEntity {
#Id
private String id;
private String userid;
private String type;
#Type(type = "json")
private String history;
private Date lastchanged;
}
Implement Hibernate ParameterizedType and UserType to ensure the conversion between the 2 types (json <->string)
public class JSONUserType implements ParameterizedType, UserType {
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final ClassLoaderService classLoaderService = new ClassLoaderServiceImpl();
public static final String JSON_TYPE = "json";
public static final String CLASS = "CLASS";
private Class jsonClassType;
#Override
public Class<Object> returnedClass() {
return Object.class;
}
#Override
public int[] sqlTypes() {
return new int[]{Types.JAVA_OBJECT};
}
#Override
public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {
try {
final String json = resultSet.getString(names[0]);
return json == null ? null : objectMapper.readValue(json, jsonClassType);
} catch (IOException e) {
throw new HibernateException(e);
}
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {
try {
final String json = value == null ? null : objectMapper.writeValueAsString(value);
PGobject pgo = new PGobject();
pgo.setType(JSON_TYPE);
pgo.setValue(json);
st.setObject(index, pgo);
} catch (JsonProcessingException e) {
throw new HibernateException(e);
}
}
#Override
public void setParameterValues(Properties parameters) {
final String clazz = (String) parameters.get(CLASS);
jsonClassType = classLoaderService.classForName(clazz);
}
#SuppressWarnings("unchecked")
#Override
public Object deepCopy(Object value) throws HibernateException {
if (!(value instanceof Collection)) {
return value;
}
Collection<?> collection = (Collection) value;
Collection collectionClone = CollectionFactory.newInstance(collection.getClass());
collectionClone.addAll(collection.stream().map(this::deepCopy).collect(Collectors.toList()));
return collectionClone;
}
static final class CollectionFactory {
#SuppressWarnings("unchecked")
static <E, T extends Collection<E>> T newInstance(Class<T> collectionClass) {
if (List.class.isAssignableFrom(collectionClass)) {
return (T) new ArrayList<E>();
} else if (Set.class.isAssignableFrom(collectionClass)) {
return (T) new HashSet<E>();
} else {
throw new IllegalArgumentException("Unsupported collection type : " + collectionClass);
}
}
}
#Override
public boolean isMutable() {
return true;
}
#Override
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y) {
return true;
}
if ((x == null) || (y == null)) {
return false;
}
return x.equals(y);
}
#Override
public int hashCode(Object x) throws HibernateException {
assert (x != null);
return x.hashCode();
}
#Override
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return deepCopy(cached);
}
#Override
public Serializable disassemble(Object value) throws HibernateException {
Object deepCopy = deepCopy(value);
if (!(deepCopy instanceof Serializable)) {
throw new SerializationException(String.format("%s is not serializable class", value), null);
}
return (Serializable) deepCopy;
}
#Override
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return deepCopy(original);
}
}
And extends PostgreSQL94Dialect class to tell the serializer the matching type:
public class JSONPostgreSQLDialect extends PostgreSQL94Dialect {
public JSONPostgreSQLDialect() {
super();
registerColumnType(Types.JAVA_OBJECT, JSONUserType.JSON_TYPE);
}
}
If you use Spring you must declare this last class in application.properties like this :
spring.jpa.database-platform=com.yourpackage.JSONPostgreSQLDialect
Postgres JSON type has been added to Hibernate in the PostgreSQL92Dialect. So you should either use that dialect or one of its subclasses, or make a custom dialect that adds the following type definition:
this.registerColumnType(2000, "json");
The type itself can be defined as follows (example for Hibernate 5.x):
public class JsonType implements UserType {
public static final ObjectMapper MAPPER = new ObjectMapper();
private int[] sqlTypes;
private com.fasterxml.jackson.databind.ObjectWriter writer;
private JavaType type;
private boolean isBinary;
private ObjectReader reader;
public JsonType() {
init(SimpleType.constructUnsafe(Object.class), false);
}
public JsonType(Class clazz, boolean isBinary) {
this(SimpleType.construct(clazz), isBinary);
}
public JsonType(JavaType type, boolean isBinary) {
init(type, isBinary);
}
protected void init(JavaType type, boolean isBinary) {
this.type = type;
this.isBinary = isBinary;
this.reader = MAPPER.readerFor(type);
this.writer = MAPPER.writerFor(type);
this.sqlTypes = new int[]{Types.JAVA_OBJECT};
}
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y) {
return true;
} else if (x == null || y == null) {
return false;
} else {
return x.equals(y);
}
}
public int hashCode(Object x) throws HibernateException {
return null == x ? 0 : x.hashCode();
}
public boolean isMutable() {
return true;
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
final Object result = rs.getObject(names[0]);
if (!rs.wasNull()) {
String content;
if (result instanceof String) {
content = (String) result;
} else if (result instanceof PGobject) {
// If we get directly the PGobject for some reason (more exactly, if a DB like H2 does the serialization directly)
content = ((PGobject) result).getValue();
} else {
throw new IllegalArgumentException("Unknown object type (excepted pgobject or json string)");
}
if (content != null) {
return convertJsonToObject(content);
}
}
return null;
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
if (value == null) {
st.setObject(index, null);
return;
}
PGobject pg = new PGobject();
pg.setType(isBinary ? "jsonb" : "json");
pg.setValue(convertObjectToJson(value));
st.setObject(index, pg);
}
Object convertJsonToObject(String content) {
try {
return reader.readValue(content);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
String convertObjectToJson(Object object) {
try {
return writer.writeValueAsString(object);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public Object deepCopy(Object value) throws HibernateException {
String json = convertObjectToJson(value);
return convertJsonToObject(json);
}
public Object replace(Object original, Object target, Object owner)
throws HibernateException {
return deepCopy(original);
}
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable) deepCopy(value);
}
public Object assemble(Serializable cached, Object owner)
throws HibernateException {
return deepCopy(cached);
}
public int[] sqlTypes() {
return sqlTypes;
}
public Class returnedClass() {
return type.getRawClass();
}
}
This example uses Jackson as a framework for JSON (de)serialization.
You can then use your type as follows:
#Entity
#TypeDefs({#TypeDef( name= "StringJsonObject", typeClass = JsonType.class)})
public class MyEntity {
#Type(type = "StringJsonObject")
#Column(name="visuals", columnDefinition = "json")
private Map<String, String> visuals;
}
But this is all very similar to the type that you implemented (presumably for Hibernate 4.x). So why wasn't your implementation working? This is because your field is actually of type json[] (a Postgres array of JSON objects). This mapper only works with JSON objects (type json). This JSON object can very well be a JSON array of JSON objects, but it has to be of type json. So you should change the type in your database schema, or implement a UserType that can work with arrays, but the first option is most likely.

How to ignore field in runtime when serializing in Jackson

I have a bean which contains a lot Boolean fields. I only want to add those fields with true values to json to save some payload. This is a feature and should be based on client's demand so it has to be done in a dynamic way. I don't think annotations will work because they are static things. Any idea on this?
In addition to the Jackson's views you can write a custom Jackson filter which would filter out the negative values of all the boolean fields.
Here is an example:
public class JacksonFilterBoolean {
#JsonFilter("boolean-filter")
public static class Test {
public final Boolean f1;
public final boolean f2;
public final boolean f3;
public final Boolean fNull = null;
public final String f4 = "string";
public Test(Boolean f1, boolean f2, boolean f3) {
this.f1 = f1;
this.f2 = f2;
this.f3 = f3;
}
}
public static class BooleanPropertyFilter extends SimpleBeanPropertyFilter {
#Override
protected boolean include(BeanPropertyWriter writer) {
return true;
}
#Override
protected boolean include(PropertyWriter writer) {
return true;
}
#Override
public void serializeAsField(Object pojo, JsonGenerator jgen,
SerializerProvider provider, PropertyWriter writer)
throws Exception {
if (writer instanceof BeanPropertyWriter) {
BeanPropertyWriter bWriter = (BeanPropertyWriter) writer;
Class<?> type = bWriter.getType().getRawClass();
if (type == Boolean.class || type == boolean.class) {
Object o = bWriter.get(pojo);
if (o != null && !(boolean) o) {
return;
}
}
}
super.serializeAsField(pojo, jgen, provider, writer);
}
}
public static void main(String[] args) throws JsonProcessingException {
Test t = new Test(true, false, true);
ObjectMapper mapper = new ObjectMapper();
mapper.setFilters(new SimpleFilterProvider().addFilter("boolean-filter",
new BooleanPropertyFilter()));
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(t));
}
}
Output:
{
"f1" : true,
"f3" : true,
"fNull" : null,
"f4" : "string"
}
Something like Jackson's JSON Views?
There's an issue open for that in Spring's issue tracker.

how to create a manytomany relationship between an entity and org.hibernate.usertype.UserType

I have an entity like this:
#Entity
#Table(name = "PLATFORM")
public class Platform{
#Id
#Column(name = "PLATFORM_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer platformId;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="PLATFORM_TYPE_ID")
private PlatformType platformType;
//must be manytomany but how?
private List<MimeType> supportedMimeTypes;
...
And I have a MimeType class which is a org.hibernate.usertype.UserType indeed.
import java.sql.Types;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class MimeType extends EnumarationType{
private static final long serialVersionUID = 1L;
public static final MimeType XML = new MimeType(new Integer(0), "XML");
public static final MimeType JSON = new MimeType(new Integer(1), "JSON");
protected static ArrayList<MimeType> list = new ArrayList<MimeType>();
static {
SQL_TYPES = new int[] { Types.INTEGER };
list.add(XML);
list.add(JSON);
}
public MimeType(){
}
public MimeType(Integer value, String label) {
super(value, label);
}
public List<?> getList() {
return list;
}
public static MimeType getById(int id) {
Iterator<MimeType> it = list.iterator();
while (it.hasNext()) {
MimeType status = it.next();
int statusId = Integer.parseInt(status.getValue());
if (statusId == id)
return status;
}
return null;
}
public static MimeType getByName(String name) {
Iterator<MimeType> it = list.iterator();
while (it.hasNext()) {
MimeType status = it.next();
String statusName = status.getLabel();
if (statusName.equalsIgnoreCase(name))
return status;
}
return null;
}
}
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Iterator;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
public abstract class EnumarationTypeBase implements UserType, Serializable {
protected static int[] SQL_TYPES = { Types.VARCHAR };
protected String label;
protected Object value;
protected String resourceKey;
public EnumarationTypeBase() {
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public abstract List<?> getList();
private Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public EnumarationTypeBase resolveFromValue(Object value) {
List<?> list = getList();
if (list == null)
return null;
EnumarationTypeBase result = null;
for (Iterator<?> itr = list.iterator(); itr.hasNext();) {
EnumarationTypeBase enm = (EnumarationTypeBase) itr.next();
if (enm.getValue().toString().equals(value.toString())) {
result = enm;
break;
}
}
return result;
}
public EnumarationTypeBase resolveFromLabel(Object label) {
List<?> list = getList();
if (list == null)
return null;
EnumarationTypeBase result = null;
for (Iterator<?> itr = list.iterator(); itr.hasNext();) {
EnumarationTypeBase enm = (EnumarationTypeBase) itr.next();
if (enm.getLabel().equals(label.toString())) {
result = enm;
break;
}
}
return result;
}
public String toString() {
return getLabel();
}
public int[] sqlTypes() {
return SQL_TYPES;
}
public Class<?> returnedClass() {
return getClass();
}
public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException,
SQLException {
value = resultSet.getString(names[0]);
return resultSet.wasNull() ? null : resolveFromValue(value);
}
public void nullSafeSet(PreparedStatement statement, Object value, int index) throws HibernateException,
SQLException {
EnumarationTypeBase enumType = (EnumarationTypeBase) value;
if (value == null) {
statement.setNull(index, sqlTypes()[0]);
} else {
statement.setString(index, enumType.getValue().toString());
}
}
public boolean equals(Object x, Object y) {
if (x == y)
return true;
if (null == x || null == y)
return false;
return x.equals(y);
}
public boolean equals(Object obj) {
if (obj instanceof EnumarationTypeBase)
return this.getValue().equals(((EnumarationTypeBase) obj).getValue());
return super.equals(obj);
}
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return cached;
}
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable) value;
}
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
public Object deepCopy(Object value) {
return value;
}
public boolean isMutable() {
return false;
}
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
public int hashCode() {
return (new Integer(this.getValue().toString())).intValue();
}
public String getResourceKey() {
if (resourceKey == null)
resourceKey = resolveFromValue(this.value).resourceKey;
return resourceKey;
}
public void setResourceKey(String resourceKey) {
this.resourceKey = resourceKey;
}
}
public abstract class EnumarationType extends EnumarationTypeBase {
protected static int[] SQL_TYPES = { Types.VARCHAR };
public EnumarationType() {
}
public EnumarationType(Integer value, String label) {
this.value = value.toString();
this.label = label;
}
public EnumarationType(String value, String label) {
this.value = value.toString();
this.label = label;
}
public EnumarationType(Integer value, String label, String resourceKey) {
this.value = value.toString();
this.label = label;
this.resourceKey = resourceKey;
}
public String getValue() {
return (String) value;
}
public BigDecimal getBigDecimalValue() {
BigDecimal tValue = new BigDecimal(getValue());
return tValue;
}
public void setValue(String value) {
this.value = value;
}
}
So how could i define a manytomany relationship between the entity Platform and non-entity usertype MimeType.
Please help.
I solved the problem. Here is the working code
...
#ElementCollection(targetClass = MimeType.class,fetch=FetchType.EAGER)
#CollectionTable(name = "PLATFORM_MIME_TYPE", joinColumns = #JoinColumn(name = "PLATFORM_ID"))
#Enumerated(EnumType.ORDINAL)
#Column(name = "MIME_TYPE_ID", columnDefinition="integer")
#Type(
type = Constants.ENUMERATION_TYPE,
parameters = {
#Parameter(
name = "enumClass",
value = "com.ba.reme.model.enums.MimeType"),
#Parameter(
name = "identifierMethod",
value = "toInt"),
#Parameter(
name = "valueOfMethod",
value = "fromInt")
}
)
private Set<MimeType> supportedMimeTypes;
...
And the MimeType enum :
public enum MimeType {
XML(1),
JSON(2),
RSS(3);
private int value;
MimeType(int value) {
this.value = value;
}
// the identifierMethod
public int toInt() {
return value;
}
// the valueOfMethod
public static MimeType fromInt(int value) {
switch(value) {
case 2: return JSON;
case 3: return RSS;
default: return XML;
}
}
public String toString() {
switch(this) {
case RSS: return "rss";
case JSON: return "json";
default: return "xml";
}
}
}