1

I got a legacy database which I want to keep as it is, but this causes some problems with hibernate.

My current problem is, that I have the field private Calendar myTradeDateTime; in my POJO and this is mapped to 2 columns.

The first column holds only the date information (e.g. '2013-05-14') and the second column holds only the time information (e.g. '10:09:12 PM'). Now I want to use hibernate to access both columns with just that 1 field. I would assume that I need some @Converter to deal with it, but I have no idea how that should be done and I couldn't find any info on this topic (2 columns into 1 field).

So my question is, how can I map 2 columns into 1 field in hibernate 5.3?

XtremeBaumer
  • 6,275
  • 3
  • 19
  • 65

4 Answers4

2

When you use hibernate native API and hbm.xml it still works like that:

    <property name="dateMitUltimo" type="org.UTDateUltimo">
        <column name="DAT_ULTIMO" />
        <column name="SL_ULTIMO" />
    </property>

With UserType like that:

public class UTDateUltimo
    implements CompositeUserType {

Using the JPA-API of hibernate this no longer works :-( Instead you can try the embedded feature. In xml this looks like:

<entity class="de.parcit.base.db.jpa.TestEmbedded" name="TestEmbedded">
    <table name="TEMBED" />

    <attributes>
        <!-- domain="ID" type="long" -->
        <id name="id" type="long">
            <!-- <generated-value strategy="TABLE" /> -->
        </id>

        <embedded name="dateMitUltimo" />

    </attributes>
</entity>

<embeddable class="de.parcit.base.db.jpa.DateJPA" access="FIELD">

    <convert converter="de.parcit.base.db.jpa.DateConverter" attribute-name="date" />

    <attributes>

        <!-- domain="Datum_Ultimo" -->
        <basic name="date">
            <column name="DAT_ULTIMO" />
        </basic>

        <!-- domain="SL_SI" -->
        <basic name="month">
            <column name="SL_ULTIMO" />
        </basic>
    </attributes>
</embeddable>

and

public class DateConverter
    implements AttributeConverter<Date, java.sql.Date> {

I didnt try this with annotations.

Update:

"embedded" can be used with annotations too. Here is an example: Java - JPA @Basic and @Embedded annotations

B_St
  • 180
  • 2
  • 9
2

As I explained in this article, JPA and Hibernate map each entity attribute to a database column. But you can use a small workaround to achieve the requested mapping.

You first need 2 entity attributes that map the 2 database columns. If you use field-based access (you annotate the attributes and not the setters), you can skip the getter and setter methods for these attributes. You can then add a 3rd attribute, which is the one you will use in your business code. You need to annotate it with @Transient and calculate its value based on the other 2 attributes.

Here's a mapping that uses this approach to map a time (postedAtTime) and a date (postedAtDate) column to the LocalDateTime postedAt attribute.

@Entity
public class Review {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String comment;

    private LocalDate postedAtDate;

    private LocalTime postedAtTime;

    @Transient
    private LocalDateTime postedAt;

    public Long getId() {
        return id;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public LocalDateTime getPostedAt() {
        if (postedAt == null) {
            this.postedAt = LocalDateTime.of(this.postedAtDate, this.postedAtTime);
        }
        return postedAt;
    }

    public void setPostedAt(LocalDateTime postedAt) {
        this.postedAt = postedAt;
        this.postedAtDate = postedAt.toLocalDate();
        this.postedAtTime = postedAt.toLocalTime();
    }
}

Please be aware, that you need to use the postedAtDate and postedAtTime attributes in your JPQL And Criteria queries. But as long as you work with the Review entity object, you don't need to know about these 2 internal attributes.

Thorben Janssen
  • 3,012
  • 9
  • 23
0

I think there is no way to do such thing in Hibernate. I suggest to map both columns and create a getter and setter as you could see below (In addition of those you already have to create):

private LocalDate tradeDate;
private LocalTime tradeTime;

and a get/set method like:

public Calendar getMyTradeDateTime(){
    LocalDateTime dateTime = LocalDateTime.of(this.tradeDate, this.tradeTime);
    Calendar c = Calendar.getInstance();
    return c.setTimeInMillis(dateTime.toEpochSecond(ZoneOffset.of("-03:00"))*1000);
}

PS: To set the zoneoffset of your VM timezone. Can get it programmatically(?).

public void setMyTradeDateTime(Calendar c) {
   LocalDateTime dateTime = LocalDateTime.ofInstant(c.toInstant(), ZoneId.systemDefault());
    this.tradeDate = dateTime.toLocalDate();
    this.tradeTime = dateTime.toLocalTime();
}
-1
@Entity
public class Review {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String comment;

    private LocalDate postedAtDate;

    private LocalTime postedAtTime;

    @Transient
    private LocalDateTime postedAt;

    public Long getId() {
        return id;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public LocalDateTime getPostedAt() {
        if (postedAt == null) {
            this.postedAt = LocalDateTime.of(this.postedAtDate, this.postedAtTime);
        }
        return postedAt;
    }

    public void setPostedAt(LocalDateTime postedAt) {
        this.postedAt = postedAt;
        this.postedAtDate = postedAt.toLocalDate();
        this.postedAtTime = postedAt.toLocalTime();
    }
}
David Buck
  • 3,752
  • 35
  • 31
  • 35
  • 1
    When answering an old question, your answer would be much more useful to other StackOverflow users if you included some context to explain how your answer helps. See: [How do I write a good answer](https://stackoverflow.com/help/how-to-answer). – David Buck Jan 15 '20 at 10:59