[SOLVED] Persisting BigDecimal[][] to MySQL database by using JPA/Hibernate

Issue

I need to persist BigDecimal[][] type to MySQL database by using Hibernate. I dont know how to do it. I tried this:

   @Getter
@Setter
@ToString
@RequiredArgsConstructor
@Embeddable
@TypeDef(name = "coordinates_array",
         typeClass = BigDecimalArrayType.class)
public class Geometry {
    private String type;
    @Type(
            type = "coordinates_array",
            parameters = @org.hibernate.annotations.Parameter(
                    name = "sql_array_type",
                    value = "BigDecimal"

            )
    )
    @Column(
            name = "coordinates",
            columnDefinition = "BigDecimal[][]"
    )
    BigDecimal[][] coordinates;}

There is no BigDecimalArrayType class. How can i define BigDecimalArrayType? Or is there anything else we can substitute for it?

Solution

As mentioned in my comment, since you do not want to save the array in a "plain text" way you can follow this way.

Use two fields, one will be persistent (part of the DB) and the other will be transient (part only of the app logic).
Persistent will be the byte[] of your CoordinateHolder and transient will be the BigDecimal[][].

Here is the class of the CoordinateHolder. As you see I am using @Transient to disallow a field to be persisted.

@Embeddable
public class CoordinateHolder implements Serializable
{
    @Transient
    private BigDecimal[][] transientCoordinates; // Will not be saved in the DB, is part only of the business logic.

    public BigDecimal[][] getTransientCoordinates()
    {
        return transientCoordinates;
    }

    public void setTransientCoordinates(BigDecimal[][] transientCoordinates)
    {
        this.transientCoordinates = transientCoordinates;
    }
}

After this let’s take a look at the entity class:

@Entity(name = "test_name")
public class TestEntity
{
    // Your id...
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;


    @Embedded
    private CoordinateHolder coordinateHolder;

    @Lob
    @Column(name = "coordinates", columnDefinition = "BLOB")
    private byte[] blobCoordinates;

    public void setCoordinates(BigDecimal[][] arr)
    {
        if (coordinateHolder == null)
        {
            coordinateHolder = new CoordinateHolder();
        }
        coordinateHolder.setTransientCoordinates(arr);
    }

    public BigDecimal[][] getCoordinates()
    {
        if (coordinateHolder == null)
        {
            return null;
        }

        return coordinateHolder.getTransientCoordinates();
    }


    // This can also be done through Entity listeners which actually do the same thing

    // Pre persist is always performed before persisting this entity
    @PrePersist
    public void prePersisting()
    {
        System.out.println("Hey I am being persisted. And now I am turning my transient coordinates into BLOB for the db");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out;
        try
        {
            out = new ObjectOutputStream(bos);
            out.writeObject(coordinateHolder);
            out.flush();
            blobCoordinates = bos.toByteArray();
        } catch (Exception e)
        {
            // Do smth with your exception...
            e.printStackTrace();
        } finally
        {
            try
            {
                bos.close();
            } catch (IOException ignored)
            {
                // ignore close exception
            }
        }
    }

    // Perform after loading...
    @PostLoad
    public void postLoading()
    {
        System.out.println("Hey I am being fetched, I am turning my BLOB into the array holder so that you can use it in your app");

        ByteArrayInputStream bis = new ByteArrayInputStream(this.getBlobCoordinates());
        ObjectInput in = null;
        try
        {
            in = new ObjectInputStream(bis);
            CoordinateHolder o = (CoordinateHolder) in.readObject();

            this.setCoordinateHolder(o);
        }
        catch (Exception e)
        {
            // Do smth with your exception...
            e.printStackTrace();
        }
        finally
        {
            try
            {
                if (in != null)
                {
                    in.close();
                }
            } catch (IOException ex)
            {
                // ignore close exception
            }
        }

    }
    ... Further getters and setters...
}

The code is described with comments and it should be understandable. This makes it possible for you to persist what you need.

Answered By – Renis1235

Answer Checked By – Timothy Miller (BugsFixing Admin)

Leave a Reply

Your email address will not be published.