一、hibernate注解的历史背景
hibernate是我们日常进行java程序开发中用的较多的一种数据持久化框架,当我们希望把java实体映射成数据库中的表时,我们一般有两种映射方案可供选择:
(1)基于xml配置文件的映射;
(2)基于注解的映射;
前一种采用配置文件的方案将配置信息从我们具体的代码中分离出来,实现了解耦,具有较高的灵活性;后一种采用注解将配置信息直接加入到实体类上,更易于观察和配置,但其缺点正是修改配置时需要修改代码,耦合性较高。从企业的实际应用上来讲,现在更多地是采用后者进行映射配置。但究其这两种方案的本质,都是基于反射的原理实现信息的配置,只是信息的载体不同罢了。
那么,hibernate的注解是怎么实现的呢?要搞懂这个问题,我们首先来实现一个自定义注解。
二、自定义注解
我们以进行jdbc数据库连接的例子来说明自定义注解的问题。
步骤1:非注解方式DBUtil
通常来讲,在一个基于JDBC开发的项目里,都会有一个DBUtil这么一个类,在这个类里统一提供连接数据库的IP地址,端口,数据库名称, 账号,密码,编码方式等信息。如例所示,在这个DBUtil类里,这些信息,就是以属性的方式定义在类里的。
步骤2:自定义注解@JDBCConfig
接下来,就要把DBUtil这个类改造成为支持自定义注解的方式。 首先创建一个注解JDBCConfig
1. 创建注解类型的时候即不使用class也不使用interface,而是使用@interface
public @interface JDBCConfig
2. 元注解
@Target({METHOD,TYPE}) 表示这个注解可以用用在类/接口上,还可以用在方法上
@Retention(RetentionPolicy.RUNTIME) 表示这是一个运行时注解,即运行起来之后,才获取注解中的相关信息,而不像基本注解如@Override 那种不用运行,在编译时eclipse就可以进行相关工作的编译时注解。
@Inherited 表示这个注解可以被子类继承
@Documented 表示当执行javadoc的时候,本注解会生成相关文档
请在学习完本知识点最后一个步骤解析注解之后,再查看 元注解,做更详尽的学习。
3. 注解元素,这些注解元素就用于存放注解信息,在解析的时候获取出来
String ip();
int port() default 3306;
String database();
String encoding();
String loginName();
String password();
代码如下:
步骤 3 : 注解方式DBUtil
有了自定义注解@JDBCConfig之后,我们就把非注解方式DBUtil改造成为注解方式DBUtil。
如例所示,数据库相关配置信息本来是以属性的方式存放的,现在改为了以注解的方式,提供这些信息了。
注: 目前只是以注解的方式提供这些信息,但是还没有解析,接下来进行解析
步骤 4 : 解析注解
接下来就通过反射,获取这个DBUtil这个类上的注解对象
JDBCConfig config = DBUtil.class.getAnnotation(JDBCConfig.class);
拿到注解对象之后,通过其方法,获取各个注解元素的值:
String ip = config.ip();
int port = config.port();
String database = config.database();
String encoding = config.encoding();
String loginName = config.loginName();
String password = config.password();
后续就一样了,根据这些配置信息得到一个数据库连接Connection实例。
代码如下:
三、仿hibernate注解
下面来做一套仿hibernate的自定义注解,并解析其信息,来演示hibernate注解方式是如何工作的
步骤1:hibernate两种配置方式
hibernate有两种配置方式,分别是*.hbm.xml 配置方式 和注解方式。 虽然方式不一样,但是都是用于解决如下问题:
1. 当前类是否实体类
2. 对应的表名称
3. 主键对应哪个属性, 自增长策略是什么,对应字段名称是什么
4. 非主键属性对应字段名称是什么
接下来,我会做一套仿hibernate的注解,并且在一个实体类Hero上运用这些注解,并通过反射解析这些注解信息,来解决上述的问题
步骤 2 : 自定义hibernate注解
参考hibernate的 注解配置方式 ,自定义5个注解,分别对应hibernate中用到的注解:
hibernate_annotation.MyEntity 对应 javax.persistence.Entity
hibernate_annotation.MyTable 对应 javax.persistence.Table
hibernate_annotation.MyId 对应 javax.persistence.Id
hibernate_annotation.MyGeneratedValue 对应 javax.persistence.GeneratedValue
hibernate_annotation.MyColumn 对应 javax.persistence.Column
MyEntity.java
MyTable.java
MyId.java
MyGeneratedValue.java
MyColumn.java
步骤3:运用在Hero对象上
像以注解方式配置Product类 那样,在Hero类上运用这些自定义注解:
当注解的方法是value的时候,给这个注解赋值时,本来应该是:
@MyColumn(value="name_")
现在可以简略一点,写为
@MyColumn("name_")
只有当名称是value的时候可以这样,其他名称如name,stratgy等不行
步骤4:解析注解
创建一个解析类ParseHibernateAnnotation ,获取Hero类上配置的注解信息,其运行结果如图所示。
思路如下:
1. 首先获取Hero.class类对象
2. 判断本类是否进行了MyEntity 注解
3. 获取注解 MyTable
4. 遍历所有的方法,如果某个方法有MyId注解,那么就记录为主键方法primaryKeyMethod
5. 把主键方法的自增长策略注解MyGeneratedValue和对应的字段注解MyColumn 取出来,并打印
6. 遍历所有非主键方法,并且有MyColumn注解的方法,打印属性名称和字段名称的对应关系。
代码如下:
总结:以上实现了最基本的hibernate注解,从中我们可以得知无论是基于xml的配置还是基于注解的配置都只是数据的一种载体,只是表现形式不同罢了,知道了这一点,相信你对hibernate会有更深的体会和认识。
更多详细内容请参考链接:注解教程