Hibernate持久层框架使用【四】hibernate的常用注解

@DynamicInsert注解

这是一个hibernate对类的注解,用于数据的动态插入,对于持久化类中的属性是否被赋值来动态生成sql语句

例如下面使用该注解来注解User类

@Entity(name="tb_user")
@DynamicInsert(false)
public class User {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "user_id")
	private int id;
	private String name;
	private int age;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

新建一个类用于测试这个注解,例如这里为table.class

这里的MySessionFactory类为自定义的工具类,参考前面的博文

Hibernate持久层框架使用【二】数据添加与修改

public class table {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		MySessionFactory mySessionFactory = new MySessionFactory();
		Session session = mySessionFactory.getSession();
		
		User user = new User();
		user.setAge(5);
		//user.setName("李白");
		
		session.save(user);
		
		mySessionFactory.commit();
		mySessionFactory.close();
	}

}

这里的MySessionFactory是前面博客写的工具类,参考前面的博客。

运行后将在数据库中生成一个tb_user表,以及保存一条数据,查看控制台可以看到打印的sql语句

Hibernate: 
    insert 
    into
        tb_user
        (age, name) 
    values
        (?, ?)

尽管上面只对age进行赋值,但是hibernate还是生成了这条对age与name列的插入语句,为了提高性能,只对我们赋值的属性进行插入,我们可以将DynamicInsert注解括号内的false改为true或者将false去掉(默认括号内值为true)

再次运行,查看控制台打印的sql语句,满足需求

Hibernate: 
    insert 
    into
        tb_user
        (age) 
    values
        (?)

主键生成策略@GeneratedValue(strategy = GenerationType)

数据表中主键的生成策略有很多种方式,这里测试一下其中的4种

第一种类型IDENTITY

这是MySQL和SqlServer的主键生成方式,在主键属性上声明此主键生成类型,每次插入数据时主键会自动递增,而无需对主键赋值

示例代码:

User.class

@Entity(name="tb_user")
public class User {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;
	private String name;
	private int age;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

以及一个测试类,PrimaryKey.class

public class PrimaryKey {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		MySessionFactory mySessionFactory = new MySessionFactory();
		Session session = mySessionFactory.getSession();
		
		User user = new User();
//		user.setId(1);
		user.setAge(10);
		user.setName("李牧");
		session.save(user);
		
		mySessionFactory.commit();
		mySessionFactory.close();
	}

}

运行后数据表tb_user中插入一条名称为“李牧”的数据,主键为1,如果再次运行会发现插入的下一条数据主键自动增长为2

第二种类型SEQUENCE

@GeneratedValue(strategy = GenerationType.SEQUENCE)这个注解是对于Oracle数据库的,因为本机没有安装Oracle,所以可以自行测试

第三种类型AUTO

@GeneratedValue(strategy = GenerationType.AUTO)

AUTO类型能够自动识别你所使用的数据库类型,再根据类型来生成主键策略(如使用mysql时生成mysql的主键自增策略,使用Oracle时使用Oracle的主键策略),将主键生成策略改为此类型,运行后生成的表插入数据同样会自动插入主键

第四种类型TABLE

@GeneratedValue(strategy = GenerationType.TABLE)

把刚刚创建的tb_user表删除,将主键生成策略使用TABLE类型,再次运行测试,可以看到数据库里多了两张表,一张是我们创建的tb_user,一张叫做hibernate_sequences,用来保存下一条主键的信息

在运行完毕后控制台打印了这些sql语句

Hibernate: 
    select
        sequence_next_hi_value 
    from
        hibernate_sequences 
    where
        sequence_name = 'tb_user' for update
            
Hibernate: 
    insert 
    into
        hibernate_sequences
        (sequence_name, sequence_next_hi_value) 
    values
        ('tb_user', ?)
Hibernate: 
    update
        hibernate_sequences 
    set
        sequence_next_hi_value = ? 
    where
        sequence_next_hi_value = ? 
        and sequence_name = 'tb_user'
Hibernate: 
    insert 
    into
        tb_user
        (age, name, id) 
    values
        (?, ?, ?)

从SQL语句中了解到,hibernater在插入数据时,会先查询出存放下一个主键的这张表(hibernate_sequences)中的sequence_next_hi_value列的值,然后再插入数据,并更新hibernate_sequences表

查看数据表,多了一条name为“李牧”的数据,Id为1

再次运行发现新插入的数据id变成了32768,这是因为Hibernate会查出hibernate_sequences表中下一个主键的值后,用它乘2的15次方作为主键,如果再插入一条数据的话,那么主键应该会是65536(对于这个主键的生成规则,其实并不需要了解...不建议使用TABLE作为主键生成策略,因为在插入数据时要做这一系列操作肯定对性能会有影响)

联合主键

如果一张表需要多个主键的话,可以为其他属性也加上@Id注解,同时还需要实现序列化对象接口(hibernate需要将多个主键进行序列化来拼接主键)

示例代码:

@Entity(name="tb_user")
public class User implements Serializable{
	@Id
	private int id;
	@Id
	private String name;
	private int age;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

列注解@Column

@Column(name="myname",length=99,unique=true)
private String name;

列注解有多个属性,这里name表示将该列的名字改为myname,length表示长度为99,unique表示唯一性约束为true

除此之外,还有其他属性

如:

-precision:有效位数

-insertable:是否允许插入

-updatable:是否运行修改

-nullable:非空约束

-scale:小数点的位数

-columnDefinition:列的定义

columnDefinition示例:

@Column(name="myname",columnDefinition="int(66) not null")
private String name;

嵌入式主键

嵌入式主键即以一个实体类作为主键,这个类中的属性作为表的列

例如下面的代码(需要实现序列化接口),其中有id和name两个属性

public class IdAndName implements Serializable{
	private int id;
	
	private String name;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

再将这个类作为主键,使用@EmbeddedId这个注解,这样id和name就是联合主键了

示例代码:

@Entity(name="tb_user")
public class User{
	
	@EmbeddedId
	private IdAndName idAndName;
	
	public IdAndName getIdAndName() {
		return idAndName;
	}
	public void setIdAndName(IdAndName idAndName) {
		this.idAndName = idAndName;
	}
	private int age;
	
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

测试类代码:

public class PrimaryKey {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		MySessionFactory mySessionFactory = new MySessionFactory();
		Session session = mySessionFactory.getSession();
		
		User user = new User();
		IdAndName idAndName = new IdAndName();
		idAndName.setId(1);
		idAndName.setName("ts");
		user.setIdAndName(idAndName);
		user.setAge(10);
		session.save(user);
		
		mySessionFactory.commit();
		mySessionFactory.close();
	}

}

运行后查看数据库可以看到数据表中有id、name、age三个列,这是因为Hibernate将IdAndName中的属性析出来作为数据列了

如果想要对IdAndName类中的属性进行别名或者使用其他限制时,除了可以直接在IdAndName类中对属性进行注解外,还可以使用@AttributeOverrides注解进行复合属性的注解

@AttributeOverrides属性重写

在上一个注解的基础上,使用@AttributeOverrides进行属性的重写,代码如下:

将IdAndName类中的id属性进行列别名,别名为"my_id",还可以指定长度等其他属性,其他属性参考上面的列注解

将IdAndName类中的name属性进行列别名,别名为"my_name",长度为99

@Entity(name="tb_user")
@DynamicUpdate(false)
public class User{
	@Id
	@Embedded
	@AttributeOverrides({@AttributeOverride(name="id",column=@Column(name="my_id",length=11))
	,@AttributeOverride(name="name",column=@Column(name="my_name",length=99))})
	private IdAndName idAndName;
	private int age;
	
	public IdAndName getIdAndName() {
		return idAndName;
	}
	public void setIdAndName(IdAndName idAndName) {
		this.idAndName = idAndName;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

测试代码:

public class PrimaryKey {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		MySessionFactory mySessionFactory = new MySessionFactory();
		Session session = mySessionFactory.getSession();
		
		User user = new User();
		user.setAge(10);
		
		IdAndName idAndName = new IdAndName();
		idAndName.setId(1);
		idAndName.setName("王翦");
		
		user.setIdAndName(idAndName);
		
		session.save(user);
		
		mySessionFactory.commit();
		mySessionFactory.close();
	}

}

大的二进制或文本注解@Lob

这个注解用来保存一些大的二进制或文本数据,例如图片(一般web项目推荐只保存图片的路径名,通过路径名来找到图片,而不是直接保存图片)

示例代码:

@Entity(name="tb_user")
@DynamicUpdate(false)
public class User{
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;
	private String name;
	private int age;
	
	@Lob
	private byte[] image;
	
	public byte[] getImage() {
		return image;
	}
	public void setImage(byte[] image) {
		this.image = image;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

其中Image属性用来保存图片

测试代码:

public class PrimaryKey {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		MySessionFactory mySessionFactory = new MySessionFactory();
		Session session = mySessionFactory.getSession();
		
		User user = new User();
		user.setId(2);
		user.setAge(10);
		user.setName("李牧");
		 
		InputStream is = PrimaryKey.class.getResourceAsStream("a2.png");
		byte[] image = new byte[is.available()];
		is.read(image);
		is.close();
		
		user.setImage(image);
		
		session.save(user);
		
		mySessionFactory.commit();
		mySessionFactory.close();
	}

}

取测试类PrimaryKey同目录下的a2.png图片写到字节数组里,调用save方法保存,保存成功后可以将数据表中的这一列数据保存出来,保存为png格式,打开图片正常显示

日期格式注解@Temporal

-TemporalType.DATE:yyyy-MM-dd 显示日期

-TemporalType.TIME:HH:mm:ss 只显示时间

-TemporalType.TIMESTAMP:yyyy-MM-dd HH:mm:ss 全日期显示

示例代码:

@Entity(name="tb_user")
public class User{
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;
	private String name;
	private int age;
	
	@Temporal(TemporalType.TIMESTAMP)
	private Date date;
	
	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

测试类代码:

public class PrimaryKey {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		MySessionFactory mySessionFactory = new MySessionFactory();
		Session session = mySessionFactory.getSession();
		
		User user = new User();
		user.setAge(10);
		user.setName("李牧");
		user.setDate(new Date());
		session.save(user);
		
		mySessionFactory.commit();
		mySessionFactory.close();
	}

}

完成

猜你喜欢

转载自blog.csdn.net/weixin_39885435/article/details/82843943