3.配置多属性的主键
<composite-id >
<key-property name="xybh" column="xybh" type=类型设置></key-property>
<key-property name="ch" column="ch" type=类型设置></key-property>
</composite-id >
2. 映射 Java 的时间, 日期类型
1). 两个基础知识:
I. 在 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 类型对应
II. 在标准 SQL 中, DATE 类型表示日期, TIME 类型表示时间, TIMESTAMP 类型表示时间戳, 同时包含日期和时间信息.
2). 如何进行映射 ?
I. 因为 java.util.Date 是 java.sql.Date, java.sql.Time 和 java.sql.Timestamp 的父类, 所以 java.util.Date
可以对应标准 SQL 类型中的 DATE, TIME 和 TIMESTAMP
II. 基于 I, 所以在设置持久化类的 Date 类型是, 设置为 java.util.Date.
III. 如何把 java.util.Date 映射为 DATE, TIME 和 TIMESTAMP ?
可以通过 property 的 type 属性来进行映射:
例如:
<property name="date" type="timestamp">
<column name="DATE" />
</property>
<property name="date" type="data">
<column name="DATE" />
</property>
<property name="date" type="time">
<column name="DATE" />
</property>
其中 timestamp, date, time 既不是 Java 类型, 也不是标准 SQL 类型, 而是 hibernate 映射类型.
1. 在 hibernate 中使用 C3P0 数据源:
1). 导入 jar 包:
hibernate-release-4.2.4.Finalliboptionalc3p0*.jar
2). 加入配置:
hibernate.c3p0.max_size: 数据库连接池的最大连接数
hibernate.c3p0.min_size: 数据库连接池的最小连接数
hibernate.c3p0.acquire_increment: 当数据库连接池中的连接耗尽时, 同一时刻获取多少个数据库连接
hibernate.c3p0.timeout: 数据库连接池中连接对象在多长时间没有使用过后,就应该被销毁
hibernate.c3p0.idle_test_period: 表示连接池检测线程多长时间检测一次池内的所有链接对象是否超时.
连接池本身不会把自己从连接池中移除,而是专门有一个线程按照一定的时间间隔来做这件事,
这个线程通过比较连接对象最后一次被使用时间和当前时间的时间差来和 timeout 做对比,进而决定是否销毁这个连接对象。
hibernate.c3p0.max_statements: 缓存 Statement 对象的数量
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> <property name="connection.username">root</property> <property name="connection.password">root</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql:///hibernate</property> <property name="show_sql">true</property> <property name="format_sql">true</property> <property name="hbm2ddl.auto">update</property> <!-- 对象删除后是否将id置为null --> <!-- <property name="use_identifier_rollback">true</property> --> <!-- 配着c3p0 --> <property name="c3p0.max_size">10</property><!-- 当请求量非常大时,最多同时允许的连接数 --> <property name="c3p0.min_size">5</property><!-- 当没有任何线程连接数据库时,池中最少的连接数 --> <property name="c3p0.acquire_increment">5</property> <!-- 当连接池中的连接不够时,一次请求多少个连接(从min-size到max-size的过程中) --> <property name="c3p0.idle_test_period">2000</property><!-- 单位:毫秒。多久检测一次链接是否超时 --> <property name="c3p0.timeout">2000</property><!-- 单位:毫秒。链接没有请求多久后删除 --> <property name="c3p0.max_statements">10</property><!-- 数据库中缓存statement的数量 --> <!-- 批量操作时一批的数量 (MySql不支持) --> <property name="jdbc.batch_size">30</property> <!-- statment读取数据时每次从数据库中取出的记录条数 (MySql不支持) --> <property name="jdbc.fetch_size">100</property> <mapping resource="model/Person.hbm.xml" /> </session-factory> </hibernate-configuration>
Person.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 2017-2-14 18:38:23 by Hibernate Tools 3.4.0.CR1 --> <hibernate-mapping package="model"><!-- hbm的由来 --> <class name="Person" table="PERSON" dynamic-update="true" dynamic-insert="true" select-before-update="false"> <id name="id" type="int" unsaved-value="null" column="ID" access="property"> <generator class="native" /> </id> <!-- access="" 默认值为property,通过geter、seter方法。若为field,则使用反射的方式访问成员变量 --> <!-- type="string"是hibernate映射类型,对应java的java.lang.String。 两种都行。 若没写会自动通过反射的方式显示别出来类型 --> <property name="name" type="string" column="NAME" length="50"> </property> <property name="qq" access="field"> <column name="QQ" /> </property> <property name="birth" type="timestamp"> <column name="BIRTH" /> </property> <!-- 派生属性 --> <property name="desc" formula="(select concat(name,':',qq) from PERSON p where p.id=id)"></property> <!-- 映射大文件 --> <!-- 字符串大文件。 这样子数据库中的类型为longtext。要想改,同下 --> <property name="content" type="text"> <column name="CONTENT"></column> </property> <!-- 二进制大文件。读取写入方式见SessionTest3 --> <property name="image"> <column name="IMAGE" sql-type="mediumblob"></column> </property> </class> </hibernate-mapping>
SessionTest1.java
package model; import java.util.Date; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; public class SessionTest1 { private SessionFactory sessionFactory; private Session session; private Transaction transaction; @Before public void init() { Configuration configuration = new Configuration().configure(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()) .buildServiceRegistry(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); session = sessionFactory.openSession(); transaction = session.beginTransaction(); } @After public void destory() { transaction.commit(); session.close(); sessionFactory.close(); } @Test public void test() { // 这里的存相当于存到缓存中,在commit中的flush时会发送将缓存同步到数据库的sql语句 session.save(new Person(10, "ji", "110", new Date())); // (!但是当Person.hbm.xml中的<generator class="native" // />时主键生成方式为根据数据库的本地方式生成时) // 执行到这里时就发送了sql,这是例外(因为要提前确定主键的值) } /* * flush方法隐含在transaction的commit中 * 在执行flush时若session缓存中的对象与数据库中得不一致会发送一条update的sql语句 * 但只有在事务提交后数据库才会更改(符合事务的概念) */ @Test public void testFlush() { // 发送select查询 Person p1 = (Person) session.get(Person.class, 1); System.out.println(p1); // 在session缓存中修改 p1.setName("jiyunfei"); // 清空session缓存 session.clear(); // 缓存中为空所以会再发select Person p2 = (Person) session.get(Person.class, 1); System.out.println(p2); // 最后的更改并不会成功 } /* * */ @Test public void testRefresh() { Person p = (Person) session.get(Person.class, 1);// 发sql语句查询 System.out.println(p); p.setName("JiYunFei");// 在缓存当中改 System.out.println(p);// 此时的值已经改了 session.refresh(p);// 再发送sql语句,使p在缓存中得值与数据库中相同 System.out.println(p);// 此时为第一次输出的值 // 最终数据库的值都没有改 } }
SessionTest2.java
package model; import java.sql.Connection; import java.sql.SQLException; import java.util.Date; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.jdbc.Work; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; public class SessionTest2 { private SessionFactory sessionFactory; private Session session; private Transaction transaction; @Before public void before() { Configuration configuration = new Configuration().configure(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()) .buildServiceRegistry(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); session = sessionFactory.openSession(); transaction = session.beginTransaction(); } @After public void after() { transaction.commit(); session.close(); sessionFactory.close(); } @Test public void testSave() { Person p = new Person("EE", "ee", new Date()); p.setId(45); session.save(p);// 对象从临时状态转为持久状态,会自动生成id 而不是45 但不会报异常 可以运行通过 // p.setId(55);//持久化对象的id不能修改 此时运行不通过 } @Test public void testPersist() { Person p = new Person("FF", "ff", new Date()); // p.setId(45); session.persist(p);// 因为修改了id 会报异常 运行不通过; 若不修改id 则和save一样 // p.setId(55);//持久化对象的id不能修改 此时运行不通过 } /** * get 和 load 区别 1.get执行时立即发送sql语句查询;该对象为class model.Person * load执行时没有发送sql语句,而是当使用了该对象时才发sql查询,该对象为一个代理类class * model.Person_$$_javassist_0 2.若在使用该对象之前session关闭: get方法可以显示出对象p * load方法会报异常org.hibernate.LazyInitializationException(懒加载) * 3.若session没有关闭,而是要得到的对象在数据库中不存在: get会显示null; * load会报异常.org.hibernate.ObjectNotFoundException */ @Test public void testGet() { Person p = (Person) session.get(Person.class, 100); // System.out.println(p.getClass()); // session.close(); System.out.println(p); } @Test public void testLoad() { Person p = (Person) session.load(Person.class, 100); // System.out.println(p.getClass()); // session.close(); System.out.println(p); // System.out.println(p.getClass()); } @Test public void testUpdate() { Person p = (Person) session.get(Person.class, 1); // p.setQq("JiYun"); // System.out.println(p);//在这里看时已经改了但执行到这时数据库中还没有改 // session.update(p); // 对于持久化对象执行到这里时不会发update语句,而是在flush时发 // System.out.println(p);//在这里看时已经改了但执行到这时数据库中还没有改 // transaction.commit(); // session.close(); // session = sessionFactory.openSession(); // transaction = session.beginTransaction(); Person pp = new Person(); pp.setId(3); session.update(pp);// 同样在flush时发sql语句 Person p1 = (Person) session.get(Person.class, 3);// 此时数据库中没有改,但输出Person // [id=3, name=null, // qq=null, // birth=null] // 对于一个临时对象,update方法是起作用的(同游离对象) // 若要update的对象的id在数据库不存在,则报错 // session缓存中不能同时有两个oid相同的对象,否则报错org.hibernate.NonUniqueObjectException /* 以上三种情况发update语句都是flush时候 */ /* * 在hbm.xml中的class标签添加属性select-before-update="true", * 会在每次发update语句之前发select查询一下是否需要 update */ } @Test public void testSaveOrUpdate() { // 当方法中的对象oid为null,则save,否则为update // 可以在hbm.xml中的id标签中设置属性unsaved-value="10" 当方法中的对象oid为10,则save,否则为update Person p = new Person(); p.setId(10); session.saveOrUpdate(p);// 会执行save,但最后数据库中id不一定为10 } @Test public void testDelete() { Person p = (Person) session.get(Person.class, 6); System.out.println(p);// Person [id=6, name=FF, qq=ff, birth=2017-02-15 // 10:36:27.0] session.delete(p);// 是在flush时发delete语句 Person pp = (Person) session.get(Person.class, 6); System.out.println(pp);// null,但此时数据库中还有,。(联想)他是以缓存中的为主 System.out.println( p);/* * 默认输出Person [id=6, name=QQ, qq=qq, birth=2017-02-15 * 10:36:27.0] 若<property * name="use_identifier_rollback">true</property> 则输出Person * [id=unsaved-value(hbm.xml中id标签的属性,没有为null), name=QQ, * qq=qq, birth=2017-02-15 10:36:27.0] */ } @Test public void testEvict() { Person p = (Person) session.get(Person.class, 6); session.evict(p);// 从session缓存中移除p p.setQq("qq");// 因为在缓存中已经被移除所以所以最后数据库不会修改 session.update(p);// 此时p为游离对象,加上这一句才会修改,详见testUpdate } @Test public void testDoWork() { session.doWork(new Work() { @Override public void execute(Connection connection) throws SQLException { System.out.println(connection);// com.mysql.jdbc.JDBC4Connection@37858383 // 在这里使用批量操作,可以使用原生的connection } }); } /** * 派生类型 */ @Test public void testFormula() { Person p = (Person) session.get(Person.class, 2); System.out.println(p.getDesc()); } }
SessionTest3.java
package model; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.sql.Blob; import java.util.Date; import org.hibernate.Hibernate; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; public class SessionTest3 { private SessionFactory sessionFactory; private Session session; private Transaction transaction; @Before public void before() { Configuration configuration = new Configuration().configure(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()) .buildServiceRegistry(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); session = sessionFactory.openSession(); transaction = session.beginTransaction(); } @After public void after() { transaction.commit(); session.close(); sessionFactory.close(); } /** * 存大文件: 若为字符串,持久化类中属性直接为String即可 若为二进制大文件,类型为sql.Blob * * @throws Exception * */ @Test public void testBlob() throws Exception { Person p = new Person("Ji", "110", new Date()); p.setContent("content"); InputStream stream = new FileInputStream(new File("127和128.jpg")); Blob image = Hibernate.getLobCreator(session).createBlob(stream, stream.available()); p.setImage(image); session.save(p); } @Test public void testReadBlob() throws Exception { Person p = (Person) session.get(Person.class, 1); InputStream in = p.getImage().getBinaryStream(); System.out.println(in.available()); System.out.println(p.getContent()); } }