J2EE--Hibernate基础笔记
因为写的是基础内容,所以在这里,(映射集合、映射组件、复合主键和联合主键,jpa annotation,关联映射,hql等等实用内容)都不会提到~
这里写的就是试用李刚《J2EE实战》那本书里的小demo的时候发生的错误。其实我发现这本书附带的例子真的是各种错误,实在是太水。
所谓Hibernate,实际上就是:PO(持久化类) = POJO(普通、传统java对象) + 映射文件。
下面直接给出书中提供的例子作为问题解析的驱动。
首先我们在Eclipse下新建一个Dynamic Web Project。
先把需要用到的jar包放入到/WebContent/WEB-INF/lib文件夹内,我们可以用Eclipse来加,不过我比较喜欢直接在文件夹中操作,复制粘贴即可,这些包我们可以在hibernate压缩包里面找到。
然后在项目工程的src文件夹内新建和编写如下文件:
其中hibernate.cfg.xml是标准的hibernate配置文件,web应用会首先搜索这个配置文件(其实是先搜索properties文件,搜不到再搜xml),News.hbm.xml文件是我们人为定义的配置文件,用来对应一个POJO类(普通Java类),从而进行数据库持久化操作。
文件夹lee里面有两个类,一个类对应数据库表,一个类有主方法,用于测试。
先看看POJO类:
package lee; public class News { //ID,自增长列 private int id; //标题 private String title; //内容 private String content; //下面是构造函数,以及getter方法和setter方法 public News() { } public void setId(int id) { this.id = id; } public int getId() { return (this.id); } public void setTitle(String title) { this.title = title; } public String getTitle() { return (this.title); } public void setContent(String content) { this.content = content; } public String getContent() { return (this.content); } }
可见,一点持久化操作都不用写,只是一个普通的JavaBean。
那它是怎么对应到数据库的,关键就在于上面两个配置文件的配置,首先看看News.hbm.xml的配置:
<?xml version="1.0" encoding="gb2312"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="lee"> <!-- 一个类,对应一个表 --> <class name="News" table="news_table"> <!--表的主键,自增长ID--> <id name="id"> <generator class="identity"/> </id> <!-- 两个普通属性 --> <property name="title"/> <property name="content"/> </class> </hibernate-mapping>
然后,配置hibernate.cfg.xml文件,在文件内将News.hbm.xml的配置内容映射进来:
<?xml version="1.0" encoding="GBK"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 数据库驱动,众所周知 --> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <!-- 数据库名 --> <property name="connection.url">jdbc:mysql://localhost/hibernate</property> <!-- 连接数据库所用的用户名 --> <property name="connection.username">nero</property> <!-- 密码 --> <property name="connection.password">123456</property> <!-- 最大连接数 --> <property name="hibernate.c3p0.max_size">20</property> <!-- 最小连接数--> <property name="hibernate.c3p0.min_size">1</property> <!-- 连接超时时长限制 --> <property name="hibernate.c3p0.timeout">5000</property> <!-- 指定缓冲池里的陈述数量限制 --> <property name="hibernate.c3p0.max_statements">100</property> <property name="hibernate.c3p0.idle_test_period">3000</property> <property name="hibernate.c3p0.acquire_increment">2</property> <property name="hibernate.c3p0.validate">true</property> <!-- 数据库方言 --> <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property> <!-- 设置是否自动创建表 --> <property name="hbm2ddl.auto">create</property> <!-- 映射所有配置文件--> <mapping resource="News.hbm.xml"/> </session-factory> </hibernate-configuration>
然后,在lee包里创建一个有main方法的主类进行测试:
public class NewsManager { public static void main(String[] args) throws Exception { //自动加载hibernate.cfg.xml Configuration conf = new Configuration().configure(); //通过Configuration对象获取工厂类对象SessionFactory SessionFactory sf = conf.buildSessionFactory(); //获取Session Session sess = sf.openSession(); //获取对话 Transaction tx = sess.beginTransaction(); //存储操作 News n = new News(); n.setTitle("test title"); n.setContent("http://www.crazyjava.org"); sess.save(n); //提交 tx.commit(); //关闭Session sess.close(); } }
可见整个过程是非常简单易懂的,按部就班操作即可。至于想做查询操作什么的,用hql就可以了,这是hibernate提供的查询方式,基本语法规则和标准SQL是相差无几的。这篇笔记不写这个。
看着好像是可以运行了,运行主类,报错。
我们在News.hbm.xml配置文件中不是指定了类对应的数据库表名是'news_table'么,但是执行主类的时候报错如下:
SEVERE: Table 'hibernate.news_table' doesn't exist
也就说这个表不存在,后来呢,我用nero这个账户登录到mysql自行创建了题目要求的数据库和表还是不行,照样报同一个错。
mysql>use hibernate;
mysql>create table news_table (id integer not null auto_increment, title varchar(255), content varchar(255), primary key (id));
编辑器它报错的时候还顺便把错误的sql语句给出来了,我看那个句子大部分是正确的,就直接把正确的部分复制粘贴创建了数据库,是可以正常创建的。
问题记录如下:
Jan 7, 2014 10:17:13 AM org.hibernate.tool.hbm2ddl.SchemaExport create
SEVERE: Unsuccessful: create table news_table (id integer not null auto_increment, title varchar(255), content varchar(255), primary key (id)) type=InnoDB
Jan 7, 2014 10:17:13 AM org.hibernate.tool.hbm2ddl.SchemaExport create
SEVERE: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'type=InnoDB' at line 1
Jan 7, 2014 10:17:13 AM org.hibernate.tool.hbm2ddl.SchemaExport execute
可见,具体的问题就出现在粗体字那部分。这个我们没办法改的,因为这个是由hibernate底层自行通过脚本实现的。那怎么办?既然问题出在自动创建数据库,那就找到自动创建数据库的配置内容:
去网上找了下关于属性"hbm2ddl.auto"的配置(当然也可以找hibernate自带的帮助文档,不过直觉觉得直接搜索更快),列出重点内容如下。
它一般有四个属性:
create : 会根据你的model类来生成表,但是每次运行都会删除上一次的表,重新生成表,哪怕2次没有任何改变
create-drop : 根据model类生成表,但是sessionFactory一关闭,表就自动删除
update : 最常用的属性,也根据model类生成表,即使表结构改变了,表中的行仍然存在,不会删除以前的行
validate : 只会和数据库中的表进行比较,不会创建新表,但是会插入新值
所以,因为hibernate它自己生成的脚本有错误,加上"hbm2ddl.auto"属性设置成了create,那么每次运行的时候先把表删除了,问题是hibernate的create表达式又是有错误的,所以每次测试都报错hibernate.news_table表不存在!实际上不是不存在,是被它反复删除。
因此,把里面的属性值改成validate,然后我们自行在数据库中创建对应的数据库和表即可,上面已经给出语法了。
更改完成后,我们再一次执行主类,成功运行后,看看控制台输出了什么信息。学习一个框架,我们不仅要知道怎么用,还要知道它具体是怎么运作的,底层是怎么实现的,对于做应用来说,可能我们不需要去实现底层的东西(效率差),但是知道这些东西对自己肯定是有利无害的,它会让你对这些框架理解得更深刻,运用也更自如。
最后给出控制台的输出信息(当然,这个是成功运行的):
/**************************************************************** 第一,读取jar类包 Jan 7, 2014 10:22:32 AM org.hibernate.cfg.Environment <clinit> INFO: Hibernate 3.2.6 Jan 7, 2014 10:22:32 AM org.hibernate.cfg.Environment <clinit> INFO: hibernate.properties not found Jan 7, 2014 10:22:32 AM org.hibernate.cfg.Environment buildBytecodeProvider INFO: Bytecode provider name : cglib Jan 7, 2014 10:22:32 AM org.hibernate.cfg.Environment <clinit> INFO: using JDK 1.4 java.sql.Timestamp handling *****************************************************************/ /******************************************************************* 第二,1、在第一步中没有找到properties文件,这个时候自动查找hibernate.cfg.xml配置文件 2、将News.hbm.xml映射进来。然后,映射lee.News类和news_table表,这个时候还没有连接数据库,只是做简单的名字映射。 3、加载数据库驱动,连接数据库; Jan 7, 2014 10:22:32 AM org.hibernate.cfg.Configuration configure INFO: configuring from resource: /hibernate.cfg.xml Jan 7, 2014 10:22:32 AM org.hibernate.cfg.Configuration getConfigurationInputStream INFO: Configuration resource: /hibernate.cfg.xml Jan 7, 2014 10:22:32 AM org.hibernate.cfg.Configuration addResource INFO: Reading mappings from resource : News.hbm.xml Jan 7, 2014 10:22:32 AM org.hibernate.cfg.HbmBinder bindRootPersistentClassCommonValues INFO: Mapping class: lee.News -> news_table Jan 7, 2014 10:22:32 AM org.hibernate.cfg.Configuration doConfigure INFO: Configured SessionFactory: null Jan 7, 2014 10:22:32 AM org.hibernate.connection.C3P0ConnectionProvider configure INFO: C3P0 using driver: com.mysql.jdbc.Driver at URL: jdbc:mysql://localhost/hibernate Jan 7, 2014 10:22:32 AM org.hibernate.connection.C3P0ConnectionProvider configure INFO: Connection properties: {user=nero, password=****} Jan 7, 2014 10:22:32 AM org.hibernate.connection.C3P0ConnectionProvider configure INFO: autocommit mode: false *********************************************************************/ /***往下我们可以看到,其实大部分工作都是由SessionFactory工厂类来完成****/ /***而最后三行,则由hbm2ddl.TableMetadata类进行操作,检测到数据库存在以后,对数据进行写入操作***/ Jan 7, 2014 10:22:32 AM com.mchange.v2.log.MLog <clinit> INFO: MLog clients using java 1.4+ standard logging. Jan 7, 2014 10:22:32 AM com.mchange.v2.c3p0.C3P0Registry banner INFO: Initializing c3p0-0.9.1 [built 16-January-2007 14:46:42; debug? true; trace: 10] Jan 7, 2014 10:22:32 AM com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager INFO: Initializing c3p0 pool... com.mchange.v2.c3p0.PoolBackedDataSource@945bcac [ connectionPoolDataSource -> com.mchange.v2.c3p0.WrapperConnectionPoolDataSource@d9eed013 [ acquireIncrement -> 2, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, debugUnreturnedConnectionStackTraces -> false, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> z8kfsx8ztjvt8c8eui08|a98932, idleConnectionTestPeriod -> 3000, initialPoolSize -> 1, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 5000, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 100, maxStatementsPerConnection -> 0, minPoolSize -> 1, nestedDataSource -> com.mchange.v2.c3p0.DriverManagerDataSource@60480c6 [ description -> null, driverClass -> null, factoryClassLocation -> null, identityToken -> z8kfsx8ztjvt8c8eui08|1d1e730, jdbcUrl -> jdbc:mysql://localhost/hibernate, properties -> {user=******, password=******} ], preferredTestQuery -> null, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false; userOverrides: {} ], dataSourceName -> null, factoryClassLocation -> null, identityToken -> z8kfsx8ztjvt8c8eui08|1b4a74b, numHelperThreads -> 3 ] Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: RDBMS: MySQL, version: 5.5.34-0ubuntu0.12.04.1 Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: JDBC driver: MySQL-AB JDBC Driver, version: mysql-connector-java-5.1.6 ( Revision: ${svn.Revision} ) Jan 7, 2014 10:22:32 AM org.hibernate.dialect.Dialect <init> INFO: Using dialect: org.hibernate.dialect.MySQLInnoDBDialect Jan 7, 2014 10:22:32 AM org.hibernate.transaction.TransactionFactoryFactory buildTransactionFactory INFO: Using default transaction strategy (direct JDBC transactions) Jan 7, 2014 10:22:32 AM org.hibernate.transaction.TransactionManagerLookupFactory getTransactionManagerLookup INFO: No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended) Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Automatic flush during beforeCompletion(): disabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Automatic session close at end of transaction: disabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: JDBC batch size: 15 Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: JDBC batch updates for versioned data: disabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Scrollable result sets: enabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: JDBC3 getGeneratedKeys(): enabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Connection release mode: auto Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Maximum outer join fetch depth: 2 Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Default batch fetch size: 1 Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Generate SQL with comments: disabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Order SQL updates by primary key: disabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Order SQL inserts for batching: disabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory createQueryTranslatorFactory INFO: Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory Jan 7, 2014 10:22:32 AM org.hibernate.hql.ast.ASTQueryTranslatorFactory <init> INFO: Using ASTQueryTranslatorFactory Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Query language substitutions: {} Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: JPA-QL strict compliance: disabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Second-level cache: enabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Query cache: disabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory createCacheProvider INFO: Cache provider: org.hibernate.cache.NoCacheProvider Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Optimize cache for minimal puts: disabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Structured second-level cache entries: disabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Statistics: disabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Deleted entity synthetic identifier rollback: disabled Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Default entity-mode: pojo Jan 7, 2014 10:22:32 AM org.hibernate.cfg.SettingsFactory buildSettings INFO: Named query checking : enabled Jan 7, 2014 10:22:32 AM org.hibernate.impl.SessionFactoryImpl <init> INFO: building session factory Jan 7, 2014 10:22:33 AM org.hibernate.impl.SessionFactoryObjectFactory addInstance INFO: Not binding factory to JNDI, no JNDI name configured Jan 7, 2014 10:22:33 AM org.hibernate.tool.hbm2ddl.SchemaValidator validate INFO: Running schema validator Jan 7, 2014 10:22:33 AM org.hibernate.tool.hbm2ddl.SchemaValidator validate INFO: fetching database metadata Jan 7, 2014 10:22:33 AM org.hibernate.tool.hbm2ddl.TableMetadata <init> INFO: table found: hibernate.news_table Jan 7, 2014 10:22:33 AM org.hibernate.tool.hbm2ddl.TableMetadata <init> INFO: columns: [content, id, title]
那我们如果操作的是有复合主键的数据库(两个或多个数据库关联)怎么办?分析上面的问题,这个是hibernate它自己提供的jar类包里面的实现出现了错误,所以你可以更换为新版本的类包试试,另一种方法就自行去设计两个(或多个)数据库的关联关系,当然这种方式是比较原始的。
没有所谓的哪种更好,看具体情况而定。
hibernate基础学习笔记暂时就是这些。实际上在企业应用中似乎jpa应用得更多,因为管理一个文件就可以了。
jpa = POJO + @Annotation
有时间会写一写。