1. @Temporal
1.1 @Temporal的作用
数据库中的Date类型,取到页面上是yyyy-MM-dd hh-mm-ss格式,利用@Temporal则可以获取自己想要的格式类型。@Temporal的定义如以下所示:
public @interface Temporal {
TemporalType value() default TIMESTAMP;
}
默认为TemporalType.TIMESTAMP类型。
TemporalType枚举类型定义如下:
public enum TemporalType {
DATE, //java.sql.Date
TIME, //java.sql.Time
TIMESTAMP //java.sql.Timestamp
}
java.sql.Date、java.sql.Time和java.sql.Timestamp这三种类型不同,它们表示时间的精确度不同。三者的区别如表所示:
类型 说明
java.sql.Date 日期型,精确到年月日,例如“2008-08-08”
java.sql.Time 时间型,精确到时分秒,例如“20:00:00”
java.sql.Timestamp 时间戳,精确到纳秒,例如“2008-08-08 20:00:00.000000001”
示例
a. 如果在某类中有Date类型的属性,数据库中存储可能是'yyyy-MM-dd hh:MM:ss'要在查询时获得年月日,在该属性上标注。
@Temporal(TemporalType.DATE)
@Column(name = "applyDate", nullable = false, length = 10)
public Date getApplyDate() {
return applyDate;
}
上述代码在页面端取值:2017-04-12
b. 如果在某类中有Date类型的属性,数据库中存储可能是'yyyy-MM-dd hh:MM:ss'要获得时分秒,在该属性上标注 @Temporal(TemporalType.TIME) 会得到形如'HH:MM:SS' 格式的日期。
@Temporal(TemporalType.TIME)
@Column(name = "applyDate", nullable = false, length = 10)
public Date getApplyDate() {
return applyDate;
}
上述代码在页面端取值:22:50:30
c. 如果在某类中有Date类型的属性,数据库中存储可能是'yyyy-MM-dd hh:MM:ss'要获得'是'yyyy-MM-dd hh:MM:ss',在该属性上标注 @Temporal(TemporalType.TIMESTAMP) 会得到形如' yyyy-MM-dd hh:MM:ss ' 格式的日期。
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "applyDate", nullable = false, length = 10)
public Date getApplyDate() {
return applyDate;
}
上述代码在页面端取值:2011-04-12 22:51:34.0
2. @Embedded和@Embeddable
在使用实体类生成对应的数据库表时,很多的时候都会遇到这种情况:在一个实体类中引用另外的实体类,一般遇上这种情况,我们使用@OneToOne、@OneToMany、@ManyToOne、@ManyToMany这4个注解比较多,但是好奇害死猫,除了这四个有没有别的使用情况,尤其是一个实体类要在多个不同的实体类中进行使用,而本身又不需要独立生成一个数据库表,这就是需要@Embedded、@Embeddable的时候了,下面分成4类来说明在一个实体类中引用另外的实体类的情况。
使用的两个实体类如下:
Address类:
public class Address implements Serializable{
private static final long serialVersionUID = 8849870114128959929L;
private String country;
private String province;
private String city;
private String detail;
//setter、getter
}
Person类:
@Entity
public class Person implements Serializable{
private static final long serialVersionUID = 8849870114127659929L;
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private Integer age;
private Address address;
//setter、getter
}
1、 两个注解全不使用
当这两个注解都不使用时,那么两个实体类和上面的相同,那么生成的表结构如下:
2、 只使用@Embeddable
我们在Address实体类上加上@Embeddable注解,变成如下类:
@Embeddable
public class Address implements Serializable{
private static final long serialVersionUID = 8849870114128959929L;
private String country;
private String province;
private String city;
private String detail;
//setter、getter
}
而Person实体类不变,生成的数据库表结构如下:
可以看出这次是把Address中的字段映射成数据库列嵌入到Person表中了,而这些字段的类型和长度也使用默认值。如果我们在Address中的字段中设置列的相关属性,则会按照我们设定的值去生成,如下Address类:
@Embeddable
public class Address implements Serializable{
private static final long serialVersionUID = 8849870114128959929L;
@Column(nullable = false)
private String country;
@Column(length = 30)
private String province;
@Column(unique = true)
private String city;
@Column(length = 50)
private String detail;
//setter、getter
}
生成的表结构如下:
我们在Address中配置的属性全部成功映射到Person表中。
3. 只使用@Embedded
这里我们只在Person中使用@Embedded,如下:
@Entity
public class Person implements Serializable{
private static final long serialVersionUID = 8849870114127659929L;
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private Integer age;
@Embedded
private Address address;
//setter、getter
}
Adddress类和最开始的不同POJO类相同,此时生成的表结构如下:
可以看出这个表结构和在Address中只使用@Embeddable注解时相同,在进入深一步试验,我们在Address中加入列属性,但是不使用@Embeddable注解会发生什么?Address类如下:
public class Address implements Serializable{
private static final long serialVersionUID = 8849870114128959929L;
@Column(nullable = false)
private String country;
@Column(length = 30)
private String province;
@Column(unique = true)
private String city;
@Column(length = 50)
private String detail;
//setter、getter
}
生成数据表结构如下:
所以只使用@Embedded和只使用@Embeddable产生的效果是相同的。
4. 两个注解全使用
既然单独使用@Embedded或者只使用@Embeddable都会产生作用,那么这两个都使用效果也一定是一样的,我们平时也是这么用的。所以在这部分我们就不演示和上面相同的效果了,而是说两个深入的话题。
4.1 覆盖@Embeddable类中字段的列属性
这里就要使用另外的两个注解@AttributeOverrides和@AttributeOverride,这两个注解是用来覆盖@Embeddable类中字段的属性的。
- @AttributeOverrides:里面只包含了@AttributeOverride类型数组;
- @AttributeOverride:包含要覆盖的@Embeddable类中字段名name和新增的@Column字段的属性;
Person类如下:
@Entity
public class Person implements Serializable{
private static final long serialVersionUID = 8849870114127659929L;
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private Integer age;
@Embedded
@AttributeOverrides(
{@AttributeOverride(name="country",
column=@Column(name = "person_country", length = 25, nullable = false)),
@AttributeOverride(name="city",
column = @Column(name = "person_city", length = 15))
})
private Address address;
//setter、getter
}
Address类如下:
@Embeddable
public class Address implements Serializable{
private static final long serialVersionUID = 8849870114128959929L;
@Column(nullable = false)
private String country;
@Column(length = 30)
private String province;
@Column(unique = true)
private String city;
@Column(length = 50)
private String detail;
//setter、getter
}
生成的数据表如下:
可以看出我们的@AttributeOverrides和@AttributeOverride两个注解起作用了。
4.2 多层嵌入实体类属性
上面所有的例子都是使用两层实体类嵌入,其实这种实体类的嵌入映射是可以使用多层的,具体的例子如下。
我们新建立一个类Direction表示方位如下:
@Embeddable
public class Direction implements Serializable{
@Column(nullable = false)
private Integer longitude;
private Integer latitude;
}
Address如下:
@Embeddable
public class Address implements Serializable{
private static final long serialVersionUID = 8849870114128959929L;
@Column(nullable = false)
private String country;
@Column(length = 30)
private String province;
@Column(unique = true)
private String city;
@Column(length = 50)
private String detail;
@Embedded
private Direction direction;
}
Person类如下:
@Entity
public class Person implements Serializable{
private static final long serialVersionUID = 8849870114127659929L;
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private Integer age;
@Embedded
@AttributeOverrides(
{@AttributeOverride(name="direction.latitude",
column=@Column(name = "person_latitude")),
@AttributeOverride(name="direction.longitude",
column = @Column(name = "person_longitude"))})
private Address address;
}
生成的数据表如下:
在上面需要注意如下几点:
- 在Person中定义Direction中的属性时,需要用”.”将所有相关的属性连接起来;
- 在Direction中longitude属性定义为not null,但是由于使用了@AttributeOverride注解,其中虽然没有定义null属性,但是这时使用的是默认的nullable属性,默认为true;
3. @Convert
介绍@Convert之前首先介绍实体属性类型转换器AttributeConverter。接口AttributeConverter<x, y>用于转化实体属性的,但id属性和关系属性不可用。它有两个方法:
- y convertToDatabaseColumn(x) 作用:将实体属性x转化为y存储到数据库中,即插入和更新操作时执行;
- x convertToEntityAttribute(y) 作用:将数据库中的字段y转化为实体属性x,即查询操作时执行
而@Convert用于注解在需要类型转换的字段上,将一个基本数据类型转换为另一个基本数据类型。
【示例】
** 示例1**
步骤一:创建StatusEnum
public enum StatusEnum {
ENABLE(1, "启用"), DISABLE(-1, "禁用"), DELETED(-2, "已删除");
StatusEnum(Integer value, String description) {
this.value = value;
this.description = description;
}
}
步骤二:实现AttributeConverter<String, Integer>接口,将代表数字与描述进行转换,其中实体类中的status字段为String类型。
public class StatusAttributeConverter implements AttributeConverter<String, Integer> {
@Override
public Integer convertToDatabaseColumn(String status) {
try {
//如果是数字,则直接返回(这里可以遍历StatusEnum的value来进一步验证)
return Integer.parseInt(status);
} catch (NumberFormatException e) {
//如果不是数字,则通过StatusEnum来找到描述对应的数字
for (StatusEnum type : StatusEnum.values()) {
if (status.equals(type.getDescription()))
return type.getValue();
}
}
//如果StatusEnum里不存在代表数字或描述,则抛出异常
throw new RuntimeException("Unknown StatusEnum: " + status);
}
@Override
public String convertToEntityAttribute(Integer value) {
//将数字转换为描述
for (StatusEnum type : StatusEnum.values()) {
if (value.equals(type.getValue())) {
return type.getDescription();
}
}
throw new RuntimeException("Unknown database value: " + value);
}
}
步骤三:实体类
@Entity
@Table(name = "t_demo")
public class DemoEntity {
@Convert(converter = StatusAttributeConverter.class)
private String status; //状态:1 启用,-1 禁用,-2 已删除
}
示例2
将一个基本数据类型转换为另一个基本数据类型。
public class Demo
{
@Column(length=2048, columnDefinition="text")
@Convert(converter=com.sys.jpa.JSONObjectSerializeConvert.class)
protected JSONObject smeta;
//getter、setter
}
@Converter
public class JSONObjectSerializeConvert
implements AttributeConverter<JSONObject, String>
{
@Override
public String convertToDatabaseColumn(JSONObject attribute)
{
return(
attribute!=null ? attribute.toJSONString() : null
);
}
@Override
public JSONObject convertToEntityAttribute(String dbData)
{
return(
dbData!=null ? JSON.parseObject(dbData) : null
);
}
}