关于使用Hibernate annotation注解映射和命名策略的方式及相关问题解决
一、首先引出以下几个观点:
1).开发项目时,不管是否使用Hibernate,都应该为项目所涉及到的表名取个前缀(如果还有特别需要也可以再加个后缀[通常在处理大数量需要动态建表的时候有加后缀的必要]),这样也能保证与他人共享数据库时不至于发生表名冲突的问题,同时方便自己维护所有相关的表。
2).在每个表中为每个字段都加个前缀也是挺有好处的:比如能避免和SQL语句关键字发生冲突的问题;在连接查询时也能方便地判断某某字段是属于某个表的。
3).使用Hibernate annotation注解映射可以减少实体类相对应的.hbm.xml文件的维护(纯粹一体力活,能省则省),只需要针对实体类进行Annotation的方式配置即可。
二、程序实验环境说明
1.导入相关jar包
1).hibernate必须用到的包(其实是3.2版本)
antlr-2.7.6.jar
asm-attrs.jar
asm.jar
cglib-2.1.3.jar
commons-collections-2.1.1.jar
commons-logging-1.0.4.jar
dom4j-1.6.1.jar
ehcache-1.2.3.jar
hibernate3.jar
jta.jar
log4j-1.2.11.jar
2).hibernate annotations相关包:
ejb3-persistence-1.0.2.GA.jar (用以持久化映射)
hibernate-annotations.jar
hibernate-commons-annotations-3.1.0.GA.jar
slf4j-api-1.5.8.jar, slf4j-log4j12-1.5.8.jar(此两个jar是写相关日志需要用到的)
3).Oracle驱动包
classes12.jar
2.开发环境
Eclipse3.5
3.数据库环境: Oracle9i
三、相关文件
1).实体类:
package com.temp.domain;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table
public class BasicCar implements Serializable{
//Table注释标识和各个成员变量的Column注释标识都未指定name属性,是为了使命名策略中相关方法生效
private static final long serialVersionUID = 6961767068555866935L;
@Id
@Column(nullable=false,length=32)
@GeneratedValue(generator="hibernate-uuid")
@GenericGenerator(name="hibernate-uuid",strategy="uuid")
private String id;
@Column(nullable=false,length=30,unique=false)
private String name;
@Column (nullable=true,length=50)
private String factory;
@Column(nullable=false)
private Date date;
/**
* 默认构造函数(做映射的必须)
*/
public BasicCar() {
}
public BasicCar(String name, String factory, Date date){
this.name=name;
this.factory=factory;
this.date=date;
}
public BasicCar(String id, String name, String factory, Date date){
this(name,factory,date);
this.id=id;
}
public String toString(){
return "id="+id+
",name="+name+
",factory="+factory+
",date="+date;
}
}
2).命名策略类
package com.temp;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.hibernate.cfg.NamingStrategy;
import org.hibernate.util.StringHelper;
public class MyNamingStrategy implements NamingStrategy {
private static MyNamingStrategy namingStrategy;
private String currentClassName="";
private String currentColumnName="";
private String needDatePostfixTables[]={"ServiceVisitLog","BasicCar"}; //需要添加时间后缀的实体类名
private MyNamingStrategy() {
}
public static MyNamingStrategy getInstance(){
if(namingStrategy==null){
namingStrategy = new MyNamingStrategy();
}
return namingStrategy;
}
//实现NamingStrategy接口方法(为每个实体映射成数据库表名时添加一个前缀)
public String classToTableName(String className) {
className = StringHelper.unqualifyEntityName(className); // 先去包名(如果有的话),取类名
this.currentClassName=className;
String tablePrefix="Test_"; //表名前缀
String tableName=tablePrefix+className;
int dptLength=needDatePostfixTables.length;
for(int i=0;i<dptLength;i++){
//根据需要进行表名后缀的添加
if(needDatePostfixTables[i].equals(className)){
tableName=tableName + getPostfixCurrentyyyyMMdd();
}
}
return tableName;
}
public String tableName(String tableName) {
return tableName;
}
//实现NamingStrategy接口方法(为每个属性映射成数据库表字段时添加一个前缀,提取每个类各单词首字母并加一个下划线做为前缀)
public String propertyToColumnName(String propertyName) {
char[] chars = this.currentClassName.toCharArray();
String columnPrefix = ""; //字段名前缀
for (int i = 0; i < chars.length; i++) {
if (Character.isUpperCase(chars[i])) {
columnPrefix=columnPrefix+chars[i];
}
}
String columnName=propertyName;
if(columnPrefix.length()>0){
columnName=columnPrefix+"_"+columnName;
}
this.currentColumnName=columnName;
return columnName;
}
public String columnName(String columnName) {
//System.out.println("columnName="+columnName);
return columnName;
}
public String logicalColumnName(String columnName, String propertyName) {
if (this.currentColumnName != null) {
return this.currentColumnName;
} else {
return propertyName;
}
}
public String collectionTableName(String string, String string1,
String string2, String string3, String string4) {
return "";
}
public String foreignKeyColumnName(String string, String string1,
String string2, String string3) {
return "";
}
public String joinKeyColumnName(String string, String string1) {
return "";
}
public String logicalCollectionColumnName(String string, String string1,
String string2) {
return "";
}
public String logicalCollectionTableName(String string, String string1,
String string2, String string3) {
return "";
}
//得到一个形如"_20100331"之类的字符串
private String getPostfixCurrentyyyyMMdd(){
Date date=new Date();
SimpleDateFormat df=new SimpleDateFormat("yyyyMMdd");
String yyyyMMdd=df.format(date);
return "_" +yyyyMMdd;
}
}
3).hibernate.cfg.xml文件
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- OracleDriver Database connection settings-->
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@192.168.10.116:1521:test</property>
<property name="connection.username">temp</property>
<property name="connection.password">temp</property>
<property name="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<property name="format_sql">false</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">update</property>
<mapping class="com.temp.domain.BasicCar"/>
</session-factory>
</hibernate-configuration>
4).测试类Test.java
package com.temp.test;
import java.util.Date;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import com.temp.MyNamingStrategy;
import com.temp.domain.BasicCar;
public class Test {
AnnotationConfiguration cfg;
SessionFactory sf;
Session session;
void doConfiguration(){
cfg = new AnnotationConfiguration(); //使用了注释的方式则创建此类
//必须放在configure语句之前命名策略才有效(同时需要注意在实体类配置时不能显示指定Table的名称和Column的名称)
cfg.setNamingStrategy(MyNamingStrategy.getInstance());
cfg.configure("/hibernate.cfg.xml");
sf=cfg.buildSessionFactory();
}
public static void main(String[] args) {
Test t=new Test();
t.doConfiguration();
t.insert();
t.query();
}
public void insert(){
session=sf.openSession();
BasicCar bc=new BasicCar("paul", "easymap", new Date());
Transaction tx=session.beginTransaction();
session.save(bc);
tx.commit();
session.close();
}
@SuppressWarnings(value = { "unchecked" })
public void query(){
session=sf.openSession();
Query query=session.createQuery("from BasicCar");
List list=query.list();
int cnt=list.size();
System.out.println("当前共有"+cnt+"条记录,最后一条记录是:"+list.get(cnt-1));
session.close();
}
}
四、相关问题说明及解决方法
如果明明在hibernate.cfg.xml文件中配置了<property name="hbm2ddl.auto">update</property>却仍然不能自动建表,则有可能是当前连接至数据库中的用户(如本例中的temp)的配额(此处的配额不是指表空间的配额)不足所造成的(这种问题本人也是在使用plsql进行create table时发现说"Ora-01536:超出了表空间users的空间限量"时才发现的),因此特别建议当使用了Hibernate或其它ORM框架之后出现了一些诡异的问题时,建议直接到数据库的控制台或plsql进行相关操作或SQL语句的执行,以找到真正的问题!
Ora-01536 是指的你建表的那个user所能使用的空间没有了,不是那个表所在的tablespace没有free space 了。你需要做的是给那个user对那个tablespace 有更多的space可以使用。
解决方法:在plsql中执行 "ALTER USER temp QUOTA UNLIMITED ON users;" 语句使得temp用户对于表空间Users无限制配额存取.
一、首先引出以下几个观点:
1).开发项目时,不管是否使用Hibernate,都应该为项目所涉及到的表名取个前缀(如果还有特别需要也可以再加个后缀[通常在处理大数量需要动态建表的时候有加后缀的必要]),这样也能保证与他人共享数据库时不至于发生表名冲突的问题,同时方便自己维护所有相关的表。
2).在每个表中为每个字段都加个前缀也是挺有好处的:比如能避免和SQL语句关键字发生冲突的问题;在连接查询时也能方便地判断某某字段是属于某个表的。
3).使用Hibernate annotation注解映射可以减少实体类相对应的.hbm.xml文件的维护(纯粹一体力活,能省则省),只需要针对实体类进行Annotation的方式配置即可。
二、程序实验环境说明
1.导入相关jar包
1).hibernate必须用到的包(其实是3.2版本)
antlr-2.7.6.jar
asm-attrs.jar
asm.jar
cglib-2.1.3.jar
commons-collections-2.1.1.jar
commons-logging-1.0.4.jar
dom4j-1.6.1.jar
ehcache-1.2.3.jar
hibernate3.jar
jta.jar
log4j-1.2.11.jar
2).hibernate annotations相关包:
ejb3-persistence-1.0.2.GA.jar (用以持久化映射)
hibernate-annotations.jar
hibernate-commons-annotations-3.1.0.GA.jar
slf4j-api-1.5.8.jar, slf4j-log4j12-1.5.8.jar(此两个jar是写相关日志需要用到的)
3).Oracle驱动包
classes12.jar
2.开发环境
Eclipse3.5
3.数据库环境: Oracle9i
三、相关文件
1).实体类:
package com.temp.domain;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table
public class BasicCar implements Serializable{
//Table注释标识和各个成员变量的Column注释标识都未指定name属性,是为了使命名策略中相关方法生效
private static final long serialVersionUID = 6961767068555866935L;
@Id
@Column(nullable=false,length=32)
@GeneratedValue(generator="hibernate-uuid")
@GenericGenerator(name="hibernate-uuid",strategy="uuid")
private String id;
@Column(nullable=false,length=30,unique=false)
private String name;
@Column (nullable=true,length=50)
private String factory;
@Column(nullable=false)
private Date date;
/**
* 默认构造函数(做映射的必须)
*/
public BasicCar() {
}
public BasicCar(String name, String factory, Date date){
this.name=name;
this.factory=factory;
this.date=date;
}
public BasicCar(String id, String name, String factory, Date date){
this(name,factory,date);
this.id=id;
}
public String toString(){
return "id="+id+
",name="+name+
",factory="+factory+
",date="+date;
}
}
2).命名策略类
package com.temp;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.hibernate.cfg.NamingStrategy;
import org.hibernate.util.StringHelper;
public class MyNamingStrategy implements NamingStrategy {
private static MyNamingStrategy namingStrategy;
private String currentClassName="";
private String currentColumnName="";
private String needDatePostfixTables[]={"ServiceVisitLog","BasicCar"}; //需要添加时间后缀的实体类名
private MyNamingStrategy() {
}
public static MyNamingStrategy getInstance(){
if(namingStrategy==null){
namingStrategy = new MyNamingStrategy();
}
return namingStrategy;
}
//实现NamingStrategy接口方法(为每个实体映射成数据库表名时添加一个前缀)
public String classToTableName(String className) {
className = StringHelper.unqualifyEntityName(className); // 先去包名(如果有的话),取类名
this.currentClassName=className;
String tablePrefix="Test_"; //表名前缀
String tableName=tablePrefix+className;
int dptLength=needDatePostfixTables.length;
for(int i=0;i<dptLength;i++){
//根据需要进行表名后缀的添加
if(needDatePostfixTables[i].equals(className)){
tableName=tableName + getPostfixCurrentyyyyMMdd();
}
}
return tableName;
}
public String tableName(String tableName) {
return tableName;
}
//实现NamingStrategy接口方法(为每个属性映射成数据库表字段时添加一个前缀,提取每个类各单词首字母并加一个下划线做为前缀)
public String propertyToColumnName(String propertyName) {
char[] chars = this.currentClassName.toCharArray();
String columnPrefix = ""; //字段名前缀
for (int i = 0; i < chars.length; i++) {
if (Character.isUpperCase(chars[i])) {
columnPrefix=columnPrefix+chars[i];
}
}
String columnName=propertyName;
if(columnPrefix.length()>0){
columnName=columnPrefix+"_"+columnName;
}
this.currentColumnName=columnName;
return columnName;
}
public String columnName(String columnName) {
//System.out.println("columnName="+columnName);
return columnName;
}
public String logicalColumnName(String columnName, String propertyName) {
if (this.currentColumnName != null) {
return this.currentColumnName;
} else {
return propertyName;
}
}
public String collectionTableName(String string, String string1,
String string2, String string3, String string4) {
return "";
}
public String foreignKeyColumnName(String string, String string1,
String string2, String string3) {
return "";
}
public String joinKeyColumnName(String string, String string1) {
return "";
}
public String logicalCollectionColumnName(String string, String string1,
String string2) {
return "";
}
public String logicalCollectionTableName(String string, String string1,
String string2, String string3) {
return "";
}
//得到一个形如"_20100331"之类的字符串
private String getPostfixCurrentyyyyMMdd(){
Date date=new Date();
SimpleDateFormat df=new SimpleDateFormat("yyyyMMdd");
String yyyyMMdd=df.format(date);
return "_" +yyyyMMdd;
}
}
3).hibernate.cfg.xml文件
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- OracleDriver Database connection settings-->
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@192.168.10.116:1521:test</property>
<property name="connection.username">temp</property>
<property name="connection.password">temp</property>
<property name="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<property name="format_sql">false</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">update</property>
<mapping class="com.temp.domain.BasicCar"/>
</session-factory>
</hibernate-configuration>
4).测试类Test.java
package com.temp.test;
import java.util.Date;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import com.temp.MyNamingStrategy;
import com.temp.domain.BasicCar;
public class Test {
AnnotationConfiguration cfg;
SessionFactory sf;
Session session;
void doConfiguration(){
cfg = new AnnotationConfiguration(); //使用了注释的方式则创建此类
//必须放在configure语句之前命名策略才有效(同时需要注意在实体类配置时不能显示指定Table的名称和Column的名称)
cfg.setNamingStrategy(MyNamingStrategy.getInstance());
cfg.configure("/hibernate.cfg.xml");
sf=cfg.buildSessionFactory();
}
public static void main(String[] args) {
Test t=new Test();
t.doConfiguration();
t.insert();
t.query();
}
public void insert(){
session=sf.openSession();
BasicCar bc=new BasicCar("paul", "easymap", new Date());
Transaction tx=session.beginTransaction();
session.save(bc);
tx.commit();
session.close();
}
@SuppressWarnings(value = { "unchecked" })
public void query(){
session=sf.openSession();
Query query=session.createQuery("from BasicCar");
List list=query.list();
int cnt=list.size();
System.out.println("当前共有"+cnt+"条记录,最后一条记录是:"+list.get(cnt-1));
session.close();
}
}
四、相关问题说明及解决方法
如果明明在hibernate.cfg.xml文件中配置了<property name="hbm2ddl.auto">update</property>却仍然不能自动建表,则有可能是当前连接至数据库中的用户(如本例中的temp)的配额(此处的配额不是指表空间的配额)不足所造成的(这种问题本人也是在使用plsql进行create table时发现说"Ora-01536:超出了表空间users的空间限量"时才发现的),因此特别建议当使用了Hibernate或其它ORM框架之后出现了一些诡异的问题时,建议直接到数据库的控制台或plsql进行相关操作或SQL语句的执行,以找到真正的问题!
Ora-01536 是指的你建表的那个user所能使用的空间没有了,不是那个表所在的tablespace没有free space 了。你需要做的是给那个user对那个tablespace 有更多的space可以使用。
解决方法:在plsql中执行 "ALTER USER temp QUOTA UNLIMITED ON users;" 语句使得temp用户对于表空间Users无限制配额存取.