posts - 495,  comments - 11,  trackbacks - 0
  2011年9月8日

> 引言
  在Jorm中,主键的生成策略主要有AUTO、UUID、GUID、FOREIGN、SEQUENCE、INCREMENT、IDENTITY、ASSIGNED,下面分别来讲述这几种策略的应用场景

> GenerationType.AUTO
  Jorm的默认主键策略,自动增长型,自增步长为1,适用数据类型int,long,如:
  private int id // 默认策略就是AUTO,故可以不写主键策略
  或
  @Id(GenerationType.AUTO) // 默认策略可以省去不写的哦~
  private int id

> GenerationType.INCREMENT
  顾名思义,增长型,适用数据类型int,long。自增步长为1
  1> 使用默认自增步长1,如:
     @Id(GenerationType.INCREMENT)
     @Column("item_id")
     private long id;
  2> 使用自定义步长,如:
     @Id(value = GenerationType.INCREMENT, incrementBy=3) // 这里自增步长为3,注意写法
     private int id;

> GenerationType.IDENTITY
  对于那些实现了自动增长的数据库,可以使用IDENTITY,如MySQL,SQL Server,PostreSQL,前提是
  MySQL数据库中建表语句定义了主键为:id(你的主键列名) int NOT NULL AUTO_INCREMENT 或
                                                                      
id(你的主键列名) bigint NOT NULL AUTO_INCREMENT
  SQL Server数据库中建表语句定义了主键为:id int identity(xx, xx) 如此类似
  PostreSQL数据库中建表语句定义了主键为:id bigserial  或  id serial
  使用例子
  @Id(GenerationType.IDENTITY)
  @Column("id")
  private long sid;

> GenerationType.UUID
  与数据库无关的策略,适用数据类型:字符串类型,适用所有数据库,长度须大于或等于32
  @Id(GenerationType.UUID)
  private String id;

> GenerationType.GUID
  与UUID有点类似,不过这个id值是又数据库来生成的,适用于数据库MySQL、PostgreSQL、SQL Server、Oracle等
  @Id(GenerationType.GUID)
  private String id;

> GenerationType.FOREIGN
  适用于一对一关系中引用了另一个对象的主键作为自己的主键的情形,如:
  @Id(GenerationType.FOREIGN)
  @Column("identity_number")
  private String identity;

> GenerationType.SEQUENCE
  这个不用多说,应用于Oracle、H2、PostgreSQL等有sequence序列功能的数据库

> GenerationType.ASSIGNED
  用户自定义生成,需要由程序员手工给主键主动赋值

 

项目地址:http://javaclub.sourceforge.net/jorm.html
下载地址:http://sourceforge.net/projects/javaclub/files/jorm/


posted @ 2011-10-10 15:17 jadmin 阅读(1490) | 评论 (3)编辑 收藏

直接上代码吧:

> Demo one
public void batch_op_one() {

    session = Jorm.getSession();
    JdbcBatcher batcher = session.createBatcher();
    batcher.addBatch("delete from t_id_auto");
    batcher.addBatch("delete from t_incre");
    batcher.addBatch("delete from t_user");
    batcher.execute();
   
    session.beginTransaction();
    long start;
    try {
        start = System.currentTimeMillis();
        String sql = "INSERT INTO t_user(sex,age,career,name,id) VALUES(?,?,?,?,?)";
        for (int i = 0; i < 100000; i++) {
            batcher.addBatch(sql, new Object[] {"男", Numbers.random(98), Strings.random(10), Strings.fixed(6), (i+1) });}
            String sqlx = "INSERT INTO t_id_auto(name, id) VALUES(?, ?)";
            for (int i = 0; i < 100000; i++) {
                batcher.addBatch(sqlx, new Object[] {Strings.fixed(6), (i+1)});
                if(i > 200) {
                    //Integer.parseInt("kkk");
                }
            }
            batcher.execute();   
            System.out.println(System.currentTimeMillis() - start);
    } catch (Exception e) {
        session.rollback();
    } finally {
        session.endTransaction();
        session.close();
    }
}

> Demo two
public void batch_op_two() {

    session = Jorm.getSession();
    session.beginTransaction();
    session.clean(User.class);
    JdbcBatcher batcher = session.createBatcher();
    batcher.setBatchSize(500);// 指定每批处理的记录数
   
    User u;
    int times = 20 * 100;
    long start = System.currentTimeMillis();
    for(int i = 0; i < times; i++) {
     String sex = (i % 2 == 0 ? "男" : "女");
     u = new User(Strings.fixed(6), sex, Numbers.random(100), Strings.random(16));
     batcher.save(u);
    }
    batcher.execute();
    session.endTransaction();
    long cost = (System.currentTimeMillis() - start);
    System.out.println("Total:" + cost);
    System.out.println("Each:" + (float) cost / times);
    session.close();
}

项目地址:http://javaclub.sourceforge.net/jorm.html
下载地址: http://sourceforge.net/projects/javaclub/files/jorm/

posted @ 2011-10-09 20:09 jadmin 阅读(1288) | 评论 (0)编辑 收藏
关系数据库不支持继承,我们可以做如下的映射,这些映射都是牺牲关系模式的范式基础的
 
1,  用一个表包含所有继承层次的所有字段,然后标识列来标示是哪个类。这种映射方法最简单,但是是违反规范化的,而且有些字段要强制为NULL值,无法保证关系数据模型的数据完整性,这种映射方式性能最高,最简单。
 
2,  每个具体类一张表(意思就是父类不需要表),所有父属性在具体类表中重复,这种映射如果要查询父类要全部扫描子类表,而且一旦父类变化,这些字表要全部变化。
 
3,  每个类一张表,表里只包含所属类的属性,然后子类和父类共享外键,这种映射避免了第2种的可怕的修改,但是查询的时候要执行连接。
posted @ 2011-09-27 09:38 jadmin 阅读(196) | 评论 (0)编辑 收藏

      在一般情况下,在新增领域对象后,都需要获取对应的主键值。使用应用层来维护主键,在一定程度上有利于程序性能的优化和应用移植性的提高。在采用数据库自增主键的方案里,如果JDBC驱动不能绑定新增记录对应的主键,就需要手工执行查询语句以获取对应的主键值,对于高并发的系统,这很容易返回错误的主键。通过带缓存的DataFieldMaxValueIncrementer,可以一次获取批量的主键值,供多次插入领域对象时使用,它的执行性能是很高的。

      我们经常使用数据的自增字段作为表主键,也即主键值不在应用层产生,而是在新增记录时,由数据库产生。这样,应用层在保存对象前并不知道对象主键值,而必须在保存数据后才能从数据库中返回主键值。在很多情况下,我们需要获取新对象持久化后的主键值。在Hibernate等ORM框架,新对象持久化后,Hibernate会自动将主键值绑定到对象上,给程序的开发带来了很多方便。 

      在JDBC 3.0规范中,当新增记录时,允许将数据库自动产生的主键值绑定到Statement或PreparedStatement中。

      使用Statement时,可以通过以下方法绑定主键值: int executeUpdate(String sql, int autoGeneratedKeys) 

      也可以通过Connection创建绑定自增值的PreparedStatement: PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) 

      当autoGeneratedKeys参数设置为Statement.RETURN_GENERATED_KEYS值时即可绑定数据库产生的主键值,设置为Statement.NO_GENERATED_KEYS时,不绑定主键值。下面的代码演示了Statement绑定并获取数据库产生的主键值的过程:

      Statement stmt = conn.createStatement();
      String sql = "INSERT INTO t_topic(topic_title,user_id) VALUES(‘测试主题’,’123’)";
      stmt.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS); // ①指定绑定表自增主键值
      ResultSet rs = stmt.getGeneratedKeys();
      if( rs.next() ) {
           intkey = rs.getInt(); // ②获取对应的表自增主键值
      }

      Spring利用这一技术,提供了一个可以返回新增记录对应主键值的方法: int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder) ,其中第二个参数类型org.springframework.jdbc.support.KeyHolder,它是一个回调接口,Spring使用它保存新增记录对应的主键,该接口的接口方法描述如下: 

      Number getKey() throws InvalidDataAccessApiUsageException;

      当仅插入一行数据,主键不是复合键且是数字类型时,通过该方法可以直接返回新的主键值。如果是复合主键,或者有多个主键返回时,该方法抛出 InvalidDataAccessApiUsageException。该方法是最常用的方法,因为一般情况下,我们一次仅插入一条数据并且主键字段类型为数字类型; 

      如果是复合主键,则列名和列值构成Map中的一个Entry。如果返回的是多个主键,则抛出InvalidDataAccessApiUsageException异常; 

      Map getKeys() throws InvalidDataAccessApiUsageException;

      如果返回多个主键,即PreparedStatement新增了多条记录,则每一个主键对应一个Map,多个Map构成一个List。

      List getKeyList(): 

      Spring为KeyHolder接口指代了一个通用的实现类GeneratedKeyHolder,该类返回新增记录时的自增长主键值。假设我们希望在新增论坛板块对象后,希望将主键值加载到对象中,则可以按以下代码进行调整:

      public voidaddForum(final Forum forum) {
            final String sql = "INSERT INTO t_forum(forum_name,forum_desc) VALUES(?,?)";
            KeyHolder keyHolder = newGeneratedKeyHolder(); // ①创建一个主键执有者
            getJdbcTemplate().update(newPreparedStatementCreator() {
                  public PreparedStatement createPreparedStatement(Connection conn) throws SQLException {
                        PreparedStatement ps = conn.prepareStatement(sql);
                        ps.setString(1, forum.getForumName());
                        ps.setString(2, forum.getForumDesc());
                        returnps;
                  }
            }, keyHolder);
            forum.setForumId(keyHolder.getKey().intValue()); // ②从主键执有者中获取主键
      }

      这样,在调用addForum(Forum forum)新增forum领域对象后,forum将拥有对应的主键值,方便后继的使用。在JDBC 3.0之前的版本中,PreparedStatement不能绑定主键,如果采用表自增键(如MySQL的auto increment或SQLServer的identity)将给获取正确的主键值带来挑战——因为你必须在插入数据后,马上执行另一条获取新增主键的查询语句。下面给出了不同数据库获取最新自增主键值的查询语句: 

posted @ 2011-09-25 14:27 jadmin 阅读(987) | 评论 (0)编辑 收藏

1) Assigned
主键由外部程序负责生成,无需Hibernate参与。


2) hilo
通过hi/lo 算法实现的主键生成机制,需要额外的数据库表保存主键生成历史状态。


3) seqhilo
与hilo 类似,通过hi/lo 算法实现的主键生成机制,只是主键历史状态保存在Sequence中,适用于支持Sequence的数据库,如Oracle。


4) increment
主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。 这种方式可能产生的问题是:如果当前有多个实例访问同一个数据库,那么由于各个实例各自维护主键状态,不同实例可能生成同样的主键,从而造成主键重复异常。因此,如果同一数据库有多个实例访问,此方式必须避免使用。


5) identity
采用数据库提供的主键生成机制。如DB2、SQL Server、MySQL中的主键生成机制。


6) sequence
采用数据库提供的sequence 机制生成主键。如Oralce 中的Sequence。


7) native
由Hibernate根据底层数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式。


8) uuid.hex
由Hibernate基于128 位唯一值产生算法生成16 进制数值(编码后以长度32 的字符串表示)作为主键。


9) uuid.string
与uuid.hex 类似,只是生成的主键未进行编码(长度16)。在某些数据库中可能出现问题(如PostgreSQL)。


10) foreign
使用外部表的字段作为主键。一般而言,利用uuid.hex方式生成主键将提供最好的性能和数据库平台适应性。
另外由于常用的数据库,如Oracle、DB2、SQLServer、MySql 等,都提供了易用的主键生成机制(Auto-Increase 字段或者Sequence)。我们可以在数据库提供的主键生成机制上,采用generator-class=native的主键生成方式。不过值得注意的是,一些数据库提供的主键生成机制在效率上未必最佳,


大量并发insert数据时可能会引起表之间的互锁。数据库提供的主键生成机制,往往是通过在一个内部表中保存当前主键状态(如对于自增型主键而言,此内部表中就维护着当前的最大值和递增量), 之后每次插入数据会读取这个最大值,然后加上递增量作为新记录的主键,之后再把这个新的最大值更新回内部表中,这样,一次Insert操作可能导致数据库内部多次表读写操作,同时伴随的还有数据的加锁解锁操作,这对性能产生了较大影响。 因此,对于并发Insert要求较高的系统,推荐采用uuid.hex 作为主键生成机制。


如果需要采用定制的主键生成算法,则在此处配置主键生成器,主键生成器须实现org.hibernate.id.IdentifierGenerator 接口

 

关键词: Hibernate  主键   主键生成方式  IdentifierGenerator

 

posted @ 2011-09-25 13:47 jadmin 阅读(1000) | 评论 (0)编辑 收藏
     摘要:   阅读全文
posted @ 2011-09-23 16:17 jadmin 阅读(1270) | 评论 (1)编辑 收藏

http://www.oschina.net/news/21642/jdbc-orm-framework-1-0-6

主要更新:
----------------------------------------
 * [35] fix: oracle下一个分页取limit数错误的bug.
 * [34] fix: oracle下检测是否支持Savepoints时,一个未捕获的异常.
 * [33] add: 对bonecp的支持
 * [32] add: 对proxool的支持
 * [31] add: 对commons-dbcp的支持
 * [30] fix: classpath没有config.properties文件会报错


posted @ 2011-09-23 10:53 jadmin 阅读(192) | 评论 (0)编辑 收藏
> 引言
有时候我们有这样的需求,对象有一个属性可能有多个值,需要在数据库中作为一个字段存储

还是以User为例,career存储多个职业

> 建表
以MySQL为例,执行下面的sql建立数据表
CREATE TABLE `t_user` (                
        `id` int(11) NOT NULL,               
        `name` varchar(50) DEFAULT NULL,     
        `sex` char(4) DEFAULT NULL,          
        `age` int(11) DEFAULT NULL,          
        `career` varchar(100) DEFAULT NULL,  
        PRIMARY KEY (`id`)                   
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

> 代码
实体类 User.java
@Entity(table = "t_user")
@PK(value 
= "id")
public class User implements Serializable {

    
/** desc */
    
private static final long serialVersionUID = -4750351638245912867L;

    @Id
    
private int id;

    
private String name;

    
private String sex;

    
private Integer age;

    @Basic(processor
=DefinedFieldProcessor.class)
    
private String[] career;

    @NoColumn
    
private int kvalue;

    
public JawaUser() {
        
super();
    }

    
public JawaUser(String name, String sex, Integer age, String[] career) {
        
super();
        
this.name = name;
        
this.sex = sex;
        
this.age = age;
        
this.career = career;
    }

    
public int getId() {
        
return id;
    }

    
public void setId(int id) {
        
this.id = id;
    }

    
public String getName() {
        
return name;
    }

    
public void setName(String name) {
        
this.name = name;
    }

    
public String getSex() {
        
return sex;
    }

    
public void setSex(String sex) {
        
this.sex = sex;
    }

    
public Integer getAge() {
        
return age;
    }

    
public void setAge(Integer age) {
        
this.age = age;
    }

    
public String[] getCareer() {
        
return career;
    }

    
public void setCareer(String[] career) {
        
this.career = career;
    }

    
public int getKvalue() {
        
return kvalue;
    }

    
public void setKvalue(int kvalue) {
        
this.kvalue = kvalue;
    }

    
public String toString() {
        
return "User [age=" + age + ", career=" + Arrays.toString(career)
                
+ ", id=" + id + ", kvalue=" + kvalue + ", name=" + name
                
+ ", sex=" + sex + "]";
    }
}
属性字段处理类 DefinedFieldProcessor.java

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.javaclub.jorm.Session;
import org.javaclub.jorm.common.CommonUtil;
import org.javaclub.jorm.common.Reflections;
import org.javaclub.jorm.jdbc.process.FieldProcessor;

public
 class DefinedFieldProcessor implements FieldProcessor {

    
public Object insert(Session session, Object entity, Field field) {
        String[] crs 
= (String[]) Reflections.getFieldValue(entity, field);
        
if(!CommonUtil.isEmpty(crs)) {
            StringBuilder sbf 
= new StringBuilder();
            
for (int i = 0; i < crs.length; i++) {
                
if(i > 0) {
                    sbf.append(
",");
                }
                sbf.append(crs[i]);
            }
            
return sbf.toString();
        }
        
return "";
    }

    
public void load(Session session, Object entity, Field field, ResultSet rs,
            
int idx) throws SQLException {
        String str 
= rs.getString(idx);
        String[] crs 
= str.split(",");
        Reflections.setFieldValue(entity, field, crs);
    }

}

> 测试

import org.javaclub.jorm.Jorm;
import org.javaclub.jorm.Session;
import org.javaclub.jorm.common.Numbers;
import org.javaclub.jorm.common.Strings;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public
 class FieldProcessorTest {

    
static Session session;

    @BeforeClass
    
public static void setUpBeforeClass() {
        session 
= Jorm.getSession();
    }

    @AfterClass
    
public static void destroy() {
        Jorm.free();
    }

    @Test
    
public void test_save() {

        session.clean(User.
class);
        User u;
        
for (int i = 0; i < 100; i++) {
            String sex 
= (i % 2 == 0 ? "" : "");
            String[] cr 
= {};
            
if(i % 3 == 0) {
                cr 
= new String[] {Strings.fixed(2), Strings.random(5), Strings.fixed(6)};
            } 
else if(i % 3 == 1) {
                cr 
= new String[] {Strings.fixed(2), Strings.random(5)};
            } 
else {
                cr 
= new String[] {Strings.fixed(2)};
            }
            u 
= new User(Strings.fixed(6), sex, Numbers.random(100), cr);
            session.save(u);
        }

        
for (int i = 0; i < 10; i++) {
            u 
= session.read(User.class, i + 1);
            System.out.println(u);
        }
    }
}
posted @ 2011-09-22 20:16 jadmin 阅读(1214) | 评论 (0)编辑 收藏

> 准备
以MySQL为例,执行下面的sql建立数据表
CREATE TABLE `t_user` (                
        `id` int(11) NOT NULL,               
        `name` varchar(50) DEFAULT NULL,     
        `sex` char(4) DEFAULT NULL,          
        `age` int(11) DEFAULT NULL,          
        `career` varchar(100) DEFAULT NULL,  
        PRIMARY KEY (`id`)                   
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

> 引入jar或maven依赖,需要jar包
gerald-jorm-1.0.5.jar 最新版本下载:http://sourceforge.net/projects/javaclub/files
commons-logging-1.1.1.jar
log4j-1.2.14.jar
mysql-connector-java-5.1.6.jar
javassist-3.11.0.GA.jar 或 cglib-nodep-2.2.2.jar (根据实际情况选择性加入)


> 配置文件
在你的java工程的classpath下建立config.properties和jdbc.cfg.xml文件
config.properties内容:
# 下面路径可以根据实际情况指定,为相对classpath的路径地址
jdbc.config.path=jdbc.cfg.xml

jdbc.cfg.xml内容:
<?xml version='1.0' encoding="UTF-8"?>
<jdbc-configuration>

  <constant name="show_sql" value="true" />
  <constant name="jdbc.batch_size" value="600" />
  <constant name="bytecode.provider" value="cglib" />
 
  <connections default="simple">
 
    <connection name="simple">
      <property name="connection.implementation">org.javaclub.jorm.jdbc.connection.impl.SimpleConnection</property>
      <property name="connection.dialect">MySQLDialect</property>
      <property name="connection.driver">com.mysql.jdbc.Driver</property>
      <property name="connection.jdbcurl">jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&amp;characterEncoding=UTF-8</property>
      <property name="connection.database">test</property>
      <property name="connection.username">root</property>
      <property name="connection.password">root</property>
    </connection>

    <connection name="c3p0">
      <property name="connection.implementation">org.javaclub.jorm.jdbc.connection.impl.PooledConnection</property>
      <property name="connection.dialect">MySQLDialect</property>
      <property name="connection.driver">com.mysql.jdbc.Driver</property>
      <property name="connection.jdbcurl">jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&amp;characterEncoding=UTF-8</property>
      <property name="connection.database">test</property>
      <property name="connection.username">root</property>
      <property name="connection.password">root</property>
      <property name="connection.pool.min">1</property>
      <property name="connection.pool.max">8</property>
      <property name="connection.test.sql">select 1</property>
    </connection>
    
  </connections>

</jdbc-configuration>


> 实体类User.java
@PK(value = "id")
@Entity(table="t_user")
public class User {
    
    @Id
    private int id;

    private String name;

    private String sex;

    private Integer age;

    private String career;
    
    @NoColumn
    private int kvalue;
    
    public User() {
        super();
    }

    public User(String name, String sex, Integer age, String career) {
        super();
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.career = career;
    }

    public User(Integer id, String name, String sex, Integer age, String career) {
        super();
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.career = career;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getCareer() {
        return career;
    }

    public void setCareer(String career) {
        this.career = career;
    }

    public int getKvalue() {
        return kvalue;
    }

    public void setKvalue(int kvalue) {
        this.kvalue = kvalue;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("[" + id + ", " + name + ", " + sex + ", " + age + ", " + career + "]");
        return sb.toString();
    }

}

这里数据库字段和java实体类User的属性在命名上是一致的,如果不一致,比如如果表创建sql为:
CREATE TABLE `t_user` (                
        `user_id` int(11) NOT NULL,               
        `user_name` varchar(50) DEFAULT NULL,     
        `sex` char(4) DEFAULT NULL,          
        `col_age` int(11) DEFAULT NULL,          
        `career_job` varchar(100) DEFAULT NULL,  
        PRIMARY KEY (`id`)                   
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

那么对应的实体User应该写成:
@PK(value = "id")
@Entity(table="t_user")
public class User {
    
    @Id
    @Column("user_id")
    private int id;

    @Column("user_name")
    private String name;
        
    // 与数据库字段命名一致,可以不指定@Column
    private String sex;

    @Column("col_age")
    private Integer age;

    @Column("career_job")
    private String career;
    
    @NoColumn
    private int kvalue;
    
    public User() {
        super();
    }

    public User(String name, String sex, Integer age, String career) {
        super();
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.career = career;
    }

    public User(Integer id, String name, String sex, Integer age, String career) {
        super();
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.career = career;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getCareer() {
        return career;
    }

    public void setCareer(String career) {
        this.career = career;
    }

    public int getKvalue() {
        return kvalue;
    }

    public void setKvalue(int kvalue) {
        this.kvalue = kvalue;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("[" + id + ", " + name + ", " + sex + ", " + age + ", " + career + "]");
        return sb.toString();
    }

}


> 对User的增删查改,UserCrudTest.java,记得引入junit-4.8.2.jar
public class UserCrudTest {

    static Session session;
    
    @BeforeClass
    public static void before() {
        session = Jorm.getSession();
    }
    
    @AfterClass
    public static void after() {
        Jorm.free();
    }
    
    @Test
    public void save_user() {
        session.clean(User.class);
        User user = null;
        for (int i = 0; i < 600; i++) {
            String sex = (i % 2 == 0 ? "男" : "女");
            user = new User(Strings.fixed(5), sex, Numbers.random(98), Strings.random(8));
            session.save(user);
        }
    }
    
    @Test // 批量保存
    public void batch_save_user() {
        session.clean(User.class);
        JdbcBatcher batcher = session.createBatcher();
        User user = null;
        for (int i = 0; i < 600; i++) {
            String sex = (i % 2 == 0 ? "男" : "女");
            user = new User(Strings.fixed(5), sex, Numbers.random(98), Strings.random(8));
            batcher.save(user);
        }
        batcher.execute();
    }
    
    @Test
    public void loadUser() {
        User user = session.read(User.class, 1);
        // 这里user是一个代理对象,因为@Entity(table="t_user", lazy = true)
        System.out.println(user.getCareer());// 发出查询sql
    }
    
    @Test
    public void deletUser() {
        User user = session.read(User.class, 1);
        if(null != user) {
            session.delete(user);
        }
        user = session.read(User.class, 1);
        System.out.println(user);
    }
    
    @Test
    public void test_update_proxy() {
        
        User u;
        u = session.read(User.class, 2);
        Assert.assertNotNull(u);
        Assert.assertTrue(u instanceof JormProxy);
        
        u.setName("Gerald.Chen");
        session.update(u);
        System.out.println(u.getName());
        u = session.read(User.class, 2);
        Assert.assertTrue("Gerald.Chen".equals(u.getName()));
    }
    
    @Test
    public void queryUser() {
        SqlParams<User> params = new SqlParams<User>();
        params.setObjectClass(User.class);
        params.setFirstResult(8);
        params.setMaxResults(20);
        List<User> users = session.list(params);
        System.out.println(users.size());
        System.out.println(users);
    }
    
}

posted @ 2011-09-21 18:42 jadmin 阅读(1407) | 评论 (5)编辑 收藏

> 特点
  1.支持多数据源管理和配置
  2.自动封装Entity
  3.支持事务
  4.支持存储过程的方便调用
  5.支持lazy加载
  6.支持分页查询
  7.支持多种数据库H2,MySQL,Oracle,PostgrSQL,SQLServer

> 要求
  1.JDK 1.5 or later
  2.如需要lazy加载,需要引入cglib或javaassit,具体可配置

> 示例
  1.添加
  Session session = Jorm.getSession();
  User u = new User("Gerald.Chen", "男", 21, "job");;
  session.save(u);

  2.删除
  session.clean(User.class);// 清空表
  session.delete(User.class, "id > 100");// 指定条件删除

  session.delete(user);

  3.查询
  User user = session.read(User.class, 1);// 根据主键加载

  // 加载第一个
  User user = session.loadFirst(User.class, "(SELECT * FROM t_user WHERE id > ?)", 88);

  // 分页查询
  SqlParams<User> params = new SqlParams<User>("SELECT * FROM t_user WHERE id > ?", new Object[] { 6 });
  params.setObjectClass(User.class);
  params.setFirstResult(3);
  params.setMaxResults(10);
  List<User> users = session.list(params);

  // 查询单个属性
  String sql = "SELECT name FROM t_user WHERE id = 28";
  String name = session.queryUniqueObject(sql);

  // 查询属性列表
  List<String> names = session.list(String.class, "SELECT name FROM t_user WHERE id > ?", 200);
  List<Integer> ages = session.list(int.class, "SELECT age FROM t_user WHERE age > 18");

  4.存储过程
  final String pro = "{? = call hello_proc(?)}";
  String r = session.call(new ProcedureCaller() {
            
     public CallableStatement prepare() throws SQLException {
    CallableStatement cs = this.getSession().getConnection().prepareCall(pro);
    cs.setString(2, "World");
    cs.registerOutParameter(1, Types.CHAR);
    return cs;
     }
            
     public String callback(CallableStatement cs) throws SQLException {
    cs.execute();
    return cs.getString(1);
     }
  });

  5.事务
  session.clean(User.class);
  User u;
  session.beginTransaction();
  try {
    for(int i = 0; i < 1000; i++) {
        String sex = (i % 2 == 0 ? "男" : "女");
        u = new User(Strings.fixed(6), sex, Numbers.random(100), Strings.random(16));
        session.save(u);
        if(i == 886) {
            Integer.parseInt("kkk");
        }
    }
    session.commit();
  } catch (Exception e) {
    session.rollback();
  } finally {
    session.endTransaction();
  }

这是一个完全基于JDBC的轻量java orm framework, 目标定位于使用方便,简单,后续会增加许多新的特性


 

项目地址:http://javaclub.sourceforge.net/jorm.html

下载地址:http://sourceforge.net/projects/javaclub/files

 

posted @ 2011-09-20 18:52 jadmin 阅读(256) | 评论 (0)编辑 收藏

> 原理

其实断点续传的原理很简单,就是在 Http 的请求上和一般的下载有所不同而已。
打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:
假设服务器域名为 wwww.sjtu.edu.cn,文件名为 down.zip。
GET /down.zip HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-
excel, application/msword, application/vnd.ms-powerpoint, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
Connection: Keep-Alive

服务器收到请求后,按要求寻找请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下:

200
Content-Length=106786028
Accept-Ranges=bytes
Date=Mon, 30 Apr 2001 12:56:11 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT

所谓断点续传,也就是要从文件已经下载的地方开始继续下载。所以在客户端浏览器传给 Web 服务器的时候要多加一条信息 -- 从哪里开始。
下面是用自己编的一个"浏览器"来传递请求信息给 Web 服务器,要求从 2000070 字节开始。
GET /down.zip HTTP/1.0
User-Agent: NetFox
RANGE: bytes=2000070-
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

仔细看一下就会发现多了一行 RANGE: bytes=2000070-
这一行的意思就是告诉服务器 down.zip 这个文件从 2000070 字节开始传,前面的字节不用传了。
服务器收到这个请求以后,返回的信息如下:
206
Content-Length=106786028
Content-Range=bytes 2000070-106786027/106786028
Date=Mon, 30 Apr 2001 12:55:20 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT

和前面服务器返回的信息比较一下,就会发现增加了一行:
Content-Range=bytes 2000070-106786027/106786028

返回的代码也改为 206 了,而不再是 200 了。

> 关键点

(1) 用什么方法实现提交 RANGE: bytes=2000070-。
当然用最原始的 Socket 是肯定能完成的,不过那样太费事了,其实 Java 的 net 包中提供了这种功能。代码如下:

URL url = new URL("http://www.sjtu.edu.cn/down.zip");
HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();

// 设置 User-Agent
httpConnection.setRequestProperty("User-Agent","NetFox");
// 设置断点续传的开始位置
httpConnection.setRequestProperty("RANGE","bytes=2000070");
// 获得输入流
InputStream input = httpConnection.getInputStream();

从输入流中取出的字节流就是 down.zip 文件从 2000070 开始的字节流。大家看,其实断点续传用 Java 实现起来还是很简单的吧。接下来要做的事就是怎么保存获得的流到文件中去了。

(2)保存文件采用的方法。
我采用的是 IO 包中的 RandAccessFile 类。
操作相当简单,假设从 2000070 处开始保存文件,代码如下:
RandomAccess oSavedFile = new RandomAccessFile("down.zip","rw");
long nPos = 2000070;
// 定位文件指针到 nPos 位置
oSavedFile.seek(nPos);
byte[] b = new byte[1024];
int nRead;
// 从输入流中读入字节流,然后写到文件中
while((nRead=input.read(b,0,1024)) > 0) {
     oSavedFile.write(b,0,nRead);

}

 

posted @ 2011-09-08 21:51 jadmin 阅读(105) | 评论 (0)编辑 收藏