Hibernate POJO类和数据库的映射文件 *.hbm.xml
1. 映射文件概述:
- POJO类和关系数据库之间的映射可以用一个XML文档来定义
- 通过POJO类的数据库映射文件,Hibernate可以理解持久化类和数据表之间的对应关系,也可以理解持久化类属性与数据表之间的对应关系
- 在运行时Hibernate将根据这个映射文件来生成各种SQL语句
- 映射文件的扩展名为.hbm.xml
- 在一个映射文件里面可以定义多个<class> </class>子节点,但是我们通常建议一个类配置一个映射文件
2. 生成主键的方式有哪些,各有什么优缺点:
- increment:
- identity
- sequence
- hilo
- native
3. 属性类型 与 Hibernate 类型以及 数据库类型 之间的映射关系
4. property
- unique="true"(唯一的,可以做为索引) update="false"(不可以更改的) index="news_index"(把TITLE列设置一个索引值) length="20"(设置字段的长度)
<property name="title" type="string"
column="TITLE" unique="true"(唯一的,可以做为索引) update="false"(不可以更改的) index="news_index"(把TITLE列设置一个索引值) length="20"(设置字段的长度)>
</property>
<property name="author" type="java.lang.String" index="news_index“(把AUTHOR列设置一个索引值)
<column name="AUTHOR" />
</property>
利用上面的设置的属性,可以得到如下的数据表:
<hibernate-mapping> <class name="hibernate.sessiondemo.News" table="NEWS"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="native" /> </id> <property name="title" type="string" column="TITLE" unique="true" update="false" index="news_index" length="20"> </property> <property name="author" type="java.lang.String" index="news_index"> <column name="AUTHOR" /> </property> <property name="date" type="java.util.Date"> <column name="DATE" /> </property> </class> </hibernate-mapping>
- formula:
- formula="(sql)" 的英文括号不能少
- sql表达式中的列名和表名都应该和数据库对应,而不是和持久化对象的属性对应
- 如果需要在formula属性中使用参数,则直接使用WHERE cur.id=id 的形式, 其中id就是 和当前持久化对象的id属性对应的列 的id值将作为参数传入。
<!-- 映射派生属性 -->
<property name="desc" formula="(SELECT concat(author, ':', title) FROM NEWS n WHERE n.id=id)">
</property>
<hibernate-mapping> <class name="hibernate.sessiondemo.News" table="NEWS"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="native" /> </id> <property name="title" type="string" column="TITLE" unique="true" update="false" index="news_index" length="20"> </property> <property name="author" type="java.lang.String" index="news_index"> <column name="AUTHOR" /> </property> <property name="date" type="java.util.Date"> <column name="DATE" /> </property> <!-- 映射派生属性 --> <property name="desc" formula="(SELECT concat(author, ':', title) FROM NEWS n WHERE n.id=id)"> </property> </class> </hibernate-mapping>
- 映射JAVA的时间 日期属性
- 两个基础知识:
- 在JAVA中,代表时间和日期的类包括: java.util.Date 和 java.util.Calendar. 此外,在JDBC API还提供了3个扩展了java.util.Date 类的子类 java.sql.Date, java.sql.Time和java.sql.Timestamp, 这三个类分别和标准SQL的 DATE TIME和TIMESTAMP 类型对应。
- 在标准的SQL 中, DATE表示日期, TIME表示时间, TIMESTAMP表示时间戳。
- 如何进行映射:
- java.util.Date是java.sql.Date, java.sql.Time和java.sql.Timestamp 的父类, 所以java.util.Date 可以和这三种类型对应 (在标准的SQL 中, DATE表示日期, TIME表示时间)
- 在设置持久化类的类型时,设置为java.util.Date, 因为这个累心可以和上面三个都兼容
- 那么如何把 java.util.Date 映射为 DATE表示日期, TIME表示时间,TIMESTAMP时间戳。
- 两个基础知识:
<property name="date" type="timestamp">
<column name="DATE" />
</property>
<property name="date" type="time">
<column name="DATE" />
</property>
<property name="date" type="date">
<column name="DATE" />
</property>
- component属性设置:具体实现参照代码
work的持久化类:
package hibernate.sessiondemo; public class Worker { private Integer id; private String name; private Pay pay; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Pay getPay() { return pay; } public void setPay(Pay pay) { this.pay = pay; } }
pay的持久化类:
package hibernate.sessiondemo; public class Pay { private int monthlyPay; private int yearPay; private int vocationWithPay; public int getMonthlyPay() { return monthlyPay; } public void setMonthlyPay(int monthlyPay) { this.monthlyPay = monthlyPay; } public int getYearPay() { return yearPay; } public void setYearPay(int yearPay) { this.yearPay = yearPay; } public int getVocationWithPay() { return vocationWithPay; } public void setVocationWithPay(int vocationWithPay) { this.vocationWithPay = vocationWithPay; } }
配置文件, 主要关注: componentshuxing
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated Feb 20, 2016 4:22:33 PM by Hibernate Tools 3.5.0.Final --> <hibernate-mapping package="hibernate.sessiondemo"> <class name="Worker" table="WORKER"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="native" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <!-- 映射组成关系 --> <component name="pay"> <!-- 指定组成关系的组件的属性 --> <property name="monthlyPay" column="MONTHLY_PAY"></property> <property name="yearPay" column="YEAR_PAY"></property> <property name="vocationWithPay" column="VOCATION_WITH_PAY"></property> </component> </class> </hibernate-mapping>
生成的数据表:
最后贴上以上所有例子的代码:
News.java
package hibernate.sessiondemo; import java.util.Date; public class News { private Integer id; private String title; private String author; private Date date; private String desc; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public News(String title, String author, Date date) { super(); this.title = title; this.author = author; this.date = date; } public News() { super(); // TODO Auto-generated constructor stub } @Override public String toString() { return "News [id=" + id + ", title=" + title + ", author=" + author + ", date=" + date + "]"; } }
Worker.java
package hibernate.sessiondemo; public class Worker { private Integer id; private String name; private Pay pay; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Pay getPay() { return pay; } public void setPay(Pay pay) { this.pay = pay; } }
Pay.java
package hibernate.sessiondemo; public class Pay { private int monthlyPay; private int yearPay; private int vocationWithPay; public int getMonthlyPay() { return monthlyPay; } public void setMonthlyPay(int monthlyPay) { this.monthlyPay = monthlyPay; } public int getYearPay() { return yearPay; } public void setYearPay(int yearPay) { this.yearPay = yearPay; } public int getVocationWithPay() { return vocationWithPay; } public void setVocationWithPay(int vocationWithPay) { this.vocationWithPay = vocationWithPay; } }
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 配置链接数据库的基本信息 --> <property name="connection.username">root</property> <property name="connection.password">86915310</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost:3306/user</property> <!-- 配置hibernate的基本信息 --> <!-- hibernate所使用的数据库方言 --> <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> <!-- 是否在控制台打印SQL --> <property name="show_sql">true</property> <!-- 是否对SQL进行格式化 --> <property name="format_sql">true</property> <!-- 指定自动生成数据表的策略 --> <property name="hbm2ddl.auto">update</property> <!-- 设置hibernate的事务隔离级别 --> <property name="connection.isolation">2</property> <!-- 在执行完delete后可以把OID 设置为空 --> <!-- <property name="use_identifier_rollback">true</property> --> <!-- 指定关联的.hbm.xml文件 --> <mapping resource="hibernate/sessiondemo/News.hbm.xml"/> <mapping resource="hibernate/sessiondemo/Worker.hbm.xml"/> </session-factory> </hibernate-configuration>
Worker.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated Feb 20, 2016 4:22:33 PM by Hibernate Tools 3.5.0.Final --> <hibernate-mapping package="hibernate.sessiondemo"> <class name="Worker" table="WORKER"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="native" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> <!-- 映射组成关系 --> <component name="pay"> <!-- 指定组成关系的组件的属性 --> <property name="monthlyPay" column="MONTHLY_PAY"></property> <property name="yearPay" column="YEAR_PAY"></property> <property name="vocationWithPay" column="VOCATION_WITH_PAY"></property> </component> </class> </hibernate-mapping>
News.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated Feb 15, 2016 9:19:20 AM by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="hibernate.sessiondemo.News" table="NEWS"> <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="native" /> </id> <property name="title" type="string" column="TITLE" unique="true" update="false" index="news_index" length="20"> </property> <property name="author" type="java.lang.String" index="news_index"> <column name="AUTHOR" /> </property> <property name="date" type="timestamp"> <column name="DATE" /> </property> <!-- 映射派生属性 --> <property name="desc" formula="(SELECT concat(AUTHOR, ':', TITLE) FROM NEWS n WHERE n.ID=ID)"> </property> </class> </hibernate-mapping>
测试类:HibernateTest
package hibernate.sessiondemo; import java.util.Date; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.boot.Metadata; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; public class HibernateTest { private SessionFactory sessionFactory; private Session session; private Transaction transaction; @Before public void init(){ // 1. 创建一个 SessionFactory 对象 StandardServiceRegistry standardServiceRegistry = new StandardServiceRegistryBuilder() .configure("hibernate.cfg.xml").build(); Metadata metadata = new MetadataSources(standardServiceRegistry).addAnnotatedClass(News.class) .addAnnotatedClassName("hibernate.sessiondemo.News").addResource("/hibernate/sessiondemo/News.hbm.xml") .getMetadataBuilder().applyImplicitNamingStrategy(ImplicitNamingStrategyJpaCompliantImpl.INSTANCE) .build(); sessionFactory = metadata.getSessionFactoryBuilder().build(); // 2. 创建一个 Session对象 session = sessionFactory.openSession(); System.out.println(session); // 3. 开启事务 transaction = session.beginTransaction(); } @After public void destroy(){ // 5. 提交事务 transaction.commit(); // 6. 关闭session session.close(); // 7. 关闭sessionFactory对象 sessionFactory.close(); } @Test public void testPropertyComponent(){ Worker worker = new Worker(); Pay pay = new Pay(); pay.setMonthlyPay(1000); pay.setYearPay(80000); pay.setVocationWithPay(10); worker.setName("Javky"); worker.setPay(pay); session.save(worker); } /** * 通过 SQL的语句来给类中的属性赋值 * 1. 首先要生成属性的setter 和 getter方法 * 2. 其次配置SQL 的 formula 属性: * <!-- 映射派生属性 --> * <property name="desc" formula="(SELECT concat(author, ':', title) FROM NEWS n WHERE n.id=id)"> * </property> * * 注意: * 1. formula="(sql)" 的英文括号不能少 * 2. sql表达式中的列名和表名都应该和数据库对应,而不是和持久化对象的属性对应 * 3. 如果需要在formula属性中使用参数,则直接使用WHERE cur.id=id 的形式, 其中id就是 和当前持久化对象的id属性对应的列 的id值将作为参数传入。 */ @Test public void testPropertyFormula(){ News news = session.get(News.class, 2); System.out.println(news.getDesc()); } /** * 在这个测是函数当中,因为title的属性被设置成了 update="false" * 所以这个测试函数中, * news.setAuthor("Jack"); 会执行成功 * news.setTitle("yy"); 则执行失败 * 但是hibernate不会报错也不会输出任何异常 * <property name="title" type="string" column="TITLE" unique="true" update="false" index="news_index" length="20"> * </property> */ @Test public void testPropertyUpdate(){ News news = session.get(News.class, 1); news.setAuthor("Jack"); news.setTitle("yy"); } @Test public void testHiloGenerator(){ News news = new News("Yibu5", "RuiDian", new Date()); session.save(news); } /** * <generator class="identity" /> * 这种配置是自增的,要求底层数据库支持这种自增的方式。 * 这种方式是线程安全的 */ @Test public void testIdentityGenerator() throws Exception{ News news = new News("Yibu4", "RuiDian", new Date()); session.save(news); Thread.sleep(5000); } /** * <generator class="increment" /> * 这种配置在每次插入之前都会先查找最大的index, 然后根据最大的index + 1 * 但是他是线程不安全的,下面的例子中如果连续运行两次函数,第一次运行线程等待5s * 当第二次插入产生时,依然获取的是上一次的max + 1 这样OID就重复了, * 第二次插入失败: Duplicate entry '36' for key 'PRIMARY' * * 开发的时候几乎不用这种生成方法,测试的时候可以使用 */ @Test public void testIncrementGenerator() throws Exception{ News news = new News("Yibu3", "RuiDian", new Date()); session.save(news); Thread.sleep(5000); } @Test public void testSave(){ News news = new News(); news.setAuthor("Cristianodadasdf"); news.setTitle("EropCup2"); news.setId(100); news.setDate(new Date()); System.out.println(news); session.save(news); System.out.println(news); } }
需要引入的库文件: