澳门游艺场9159-9159金沙游戏场

并且可以在delete语句中控制每批删除的数据量

作者: 澳门游艺场  发布:2020-03-02

博主做过比较多项目的archive脚本编写,对于这种删除数据的脚本开发,肯定是一开始的话用最简单的一个delete语句,然后由于部分表数据量比较大啊,索引比较多啊,会发现删除数据很慢而且影响系统的正常使用。然后就对delete语句进行按均匀数据量分批delete的改写,这样的话,原来的删除一个表用一个语句,就可能变成几十行,如果archive的表有十几个甚至几十个,那我们的脚本篇幅就非常大了,增加了开发和维护的成本,不利于经验比较少的新入职同事去开发archive脚本,也容易把注意力分散到所谓分批逻辑中。

Ok,今天比较详细的学习一下hibernate的C(create)、R(read)、U(update)、D(delete) 相关api...

通常意义上的三层架构就是将整个业务应用划分为:表现层(UI**业务逻辑层(BLL数据访问层(**DAL)

根据这种情况,本周博主刚好在工作过程中,总结并编写了一个自动分批删除数据的模板,模板固定不变,只需要把注意力集中放在delete语句中,并且可以在delete语句中控制每批删除的数据量,比较方便,通过变量组装模板sql,避免每个表就单独写一个分批逻辑的重复代码,化简为繁,增加分批删除一个表指定数据的话只需要增加几行代码就可以。

前言

  Session: 是Hibernate持久化操作的基础,提供了众多的数据库操作方法,如save(),update(),delete()。。。etc,用于完成对象的增加,修改,删除等方法.

  后面代码中使用到的HinernateUtil类:是用于构建SessionFactory(Hibernate提供的获取session的工厂类)的一个封装类,在前面的文章SSH初体验系列--Hibernate--1--环境配置及demo中有详细代码,可自行查看.

区分层次的目的即为了“高内聚,低耦合”的思想。

demo1:不带参数,根据表tmp_Del删除表A对应ID的数据。

一) 增(C)

表现层(UI):通俗讲就是展现给用户的界面,即用户在使用一个系统的时候的所见所得。

demo2:带参数,根据Date字段是否过期删除表B对应数据。

  1.添加单条个数据;

9159金沙游戏场 19159金沙游戏场 2

    @org.junit.Test
    public void save(){
        //创建对象
        Feedback newItem=new Feedback();
        newItem.setUsername("andew17");
        newItem.setContent("测试单条插入");
        newItem.setSendTime(new Timestamp(System.currentTimeMillis()));

        Session session=null;
        Transaction tx=null;
        try{
            //获取session对象
            session= HibernateUtil.openSession();
            //开启事务
            tx=session.beginTransaction();
            //执行保存
            session.save(newItem);
            //提交事务
            tx.commit();
        }catch(Exception e){
            //异常时,事务回滚
            tx.rollback();
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            //释放session
            session.close();
        }
    }

9159金沙游戏场 ,添加单条数据

业务逻辑层(BLL):针对具体问题的操作,也可以说是对数据层的操作,澳门游艺场9159 ,对数据业务逻辑处理

具体请参考下面的脚本和相关说明,如有不懂的地方欢迎评论或私信咨询博主。

  2.批量添加数据;

    可以通过for循环,将数据统一添加到一级缓存session中,再commit到数据库;

9159金沙游戏场 39159金沙游戏场 4

@org.junit.Test
    public void saveMany(){
        //创建对象
        List<Feedback> feedbackList=new ArrayList<>();
        for(int i=0;i<10;i++){
            Feedback newItem=new Feedback();
            newItem.setUsername("andew"+i);
            newItem.setContent("测试单条插入"+i);
            newItem.setSendTime(new Timestamp(System.currentTimeMillis()));
            feedbackList.add(newItem);
        }
        Session session=null;
        Transaction tx=null;
        try{
            //获取session对象
            session= HibernateUtil.openSession();
            //开启事务
            tx=session.beginTransaction();
            //执行保存,此时只是保存到Session缓存中,并没有同步到数据库
            Feedback feedback=null;
            for(int i=0;i<feedbackList.size();i++){
                feedback=feedbackList.get(i);
                session.save(feedback);
            }
            //提交事务,就缓存同步到数据库
            tx.commit();
        }catch(Exception e){
            //异常时,事务回滚
            tx.rollback();
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            //释放session
            session.close();
        }
    }

for循环执行session的save方法

    如果数据量不大时,或者对性能没有极致的追求时,这种方法还ok;但是当批量插入数据量较大时,这个方式的弊端就体现出来了.

    弊端主要有两点:a,循环向Session添加,修改数据时,Session对象自身开辟的一级缓存会不断被消耗,直到耗尽(outOfMemoryError);

             b,通过在hibernate.cfg.xml文件配置"显示sql语句"

        <!--显示SQL语句-->
        <property name="show_sql">true</property>

              再跟踪打印的SQl语句,很容易就发现,内部其实还是执行了n(n=数据量)次的插入操作,而不是一次语句执行,在性能上,可想并不是很好. 对于喜欢追求代码性能的小伙伴,这是让人难以忍受的,如鲠在喉...

Hibernate: select max(id) from feedback
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Hibernate: insert into feedback (username, content, sendTime, id) values (?, ?, ?, ?)
Disconnected from the target VM, address: '127.0.0.1:59399', transport: 'socket'

Process finished with exit code 0

    解决方案:a,对于内存溢出,可以在代码中进行判断,每隔n条,释放一次缓存;并且在hibernate.xfg.xml中配置每次提交sql的数量

<property name="hibernate.jdbc.batch_size">10</property>

// 每处理20条清空缓存
session.save(newItem);
if (i%20 == 0) {
    session.flush();
    session.clear();
}

        b.若想在性能上有所提升,可以绕过Hibernate Api,直接使用jdbc api来做批量插入;

9159金沙游戏场 59159金沙游戏场 6

@org.junit.Test
    public void saveJdbc(){
        //创建对象
        List<Feedback> feedbackList=new ArrayList<>();
        for(int i=0;i<20;i++){
            Feedback newItem=new Feedback();
            newItem.setUsername("andew"+i);
            newItem.setContent("测试单条插入"+i);
            newItem.setSendTime(new Timestamp(System.currentTimeMillis()));
            feedbackList.add(newItem);
        }
        String insertSql="insert into feedback (username, content, sendTime) values (?, ?, ?)";

        Session session=null;
//        Transaction tx=null;
        try{
            session= HibernateUtil.openSession();
            //tx=session.beginTransaction();
            session.doWork(new Work() {
                @Override
                public void execute(Connection connection) throws SQLException {
                    //这里面就得到connection了
                    PreparedStatement stmt=connection.prepareStatement(insertSql);
                    //方式1:自动提交
                    /*connection.setAutoCommit(true);
                    for(int i=0;i<feedbackList.size();i++){
                        stmt.setString(1,"andrew"+1);
                        stmt.setString(2,"test content "+i);
                        stmt.setTimestamp(3,new Timestamp(System.currentTimeMillis()));
                        stmt.execute();//此语句,每次执行,都会将一条数据插入到db
                    }*/

                    //方式2:批量提交
                    connection.setAutoCommit(false);
                    for(int i = 0; i<feedbackList.size();i++) {
                        stmt.setString(1,"andrew"+1);
                        stmt.setString(2,"test content "+i);
                        stmt.setTimestamp(3,new Timestamp(System.currentTimeMillis()));
                        stmt.addBatch();
                        if (i % 10 == 0) {
                            stmt.executeBatch();
                            connection.commit();//此处执行一次db插入操作
                        }
                    }
                    stmt.executeBatch();
                    connection.commit();
                }
            });
            ////注意:此时不能再用事务,会报错:org.hibernate.TransactionException: Unable to commit against JDBC Connection
            // tx.commit();
        }catch(Exception e){
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            //释放session
            session.close();
        }
    }

jdbc批量插入数据

    其中有几个地方需要注意:

      a,connection对象的获取:

a)过时方式:session.connection();
     但是这个方法Hibernate不推荐使用,The method connection() from the type         Session is deprecated
    在3.3以后的版本中已经被废除了。

b)Session.doWork;
    3.3官方的替代方法是用Session.doWork(Work work);
    传入的参数work是一个接口,可以:
    HibernateFactory.getSession().doWork(
          new Work() {
            public void execute(Connection connection) {
              // 这里面就得到connection了,    
                }
          }
        );   

      b.代码中的"方式2,批量提交",就是我们最终想要的高性能的答案;

数据访问层(DAL):该层所做事务直接操作数据库,针对数据的增添、删除、修改、更新、查找等每层之间是一种垂直的关系

-- ===== 1 分批archive模板 =======================================================--/* 说明:1. 组装的archive语句为:@sql = @sql_Part1 + @sql_Del + @sql_Part22. 组装的参数@parameters为:@parameters = @parameters_Base + 自定义参数3. 传入参数:@strStepInfo 需要print的step信息4. archive逻辑专注于@sql_Del,而非分散于分批。*/declare @parameters nvarchar(max) = '', @parameters_Base nvarchar(max) = N'@strStepInfo nvarchar(100)', @sql nvarchar(max) = '', @sql_Part1 nvarchar(max) = N'declare @iBatch int = 1, --批次 @iRowCount int = -1 --删除行数,初始为-1,后面取每批删除行数@@ROWCOUNTprint convert(varchar(50), getdate(), 121) + @strStepInfowhile @iRowCount  0begin print ''begin batch:'' print @iBatch print convert(varchar(50), getdate(), 121) begin try begin tran', @sql_Del nvarchar(max) = '' --@sql_Del脚本需要根据实际情况在后续脚本中自行编写, @sql_Part2 nvarchar(max) = N' select @iRowCount = @@rowcount commit tran end try begin catch rollback tran print ''-- Error Message:'' + convert(varchar, error_line()) + '' | '' + error_message() end catch waitfor delay ''0:00:01'' --延时 print convert(varchar(50), getdate(), 121) print ''end batch'' select @iBatch = @iBatch + 1end'-- ===== 2 demo1:archive 表A =======================================================select @parameters = @parameters_Base + '' --如果有需要增加自定义参数,在这里加,例如@parameters = @parameters_Base + ', @ArchiveDate datetime', @sql_Del = ' delete top (50000) tc_Del from 表A tc_Del inner join tmp_Del cd on cd.ID = tc_Del.ID'select @sql = @sql_Part1 + @sql_Del + @sql_Part2print @sqlexec sp_executesql @sql, @parameters, N' 2 archive 表A'-- ===== 3 demo2:archive 表B =======================================================select @parameters = @parameters_Base + ', @ArchiveDaate datetime' --如果有需要增加自定义参数,在这里加,例如@parameters = @parameters_Base + ', @ArchiveDate datetime', @sql_Del = ' delete top (50000) from 表B where Date  @ArchiveDate'select @sql = @sql_Part1 + @sql_Del + @sql_Part2print @sqlexec sp_executesql @sql, @parameters, N' 3 archive 表B', @ArchiveDate

  3.使用一对一关联添加数据

    在实际开发中,我们经常会遇到表关联的情况,比如一张数据表的记录与另一张数据表记录一一对应,即:一一对应关系,此时我们对表数据的添加最好也应该是一次性关联添加的.Hibernate就提供了相关的方法,供开发人员处理这种关系的映射,下面就来学习一下.

    此处,我们使用两张新表来进行学习,UserInfo和UserExtend,其中UserInfo记录用户的姓名,性别,出生日期等基础信息,UserExtend记录扩充信息,如职业,公司,地址等。UserInfo中通过一个UserExtend属性与UserExtend表相关联。具体如下:

9159金沙游戏场 79159金沙游戏场 8

/**
 * Created by c-yangx on 11/18/2016.
        */
public class UserInfo {
    private int id;
    private String name;
    private Timestamp birthday;
    private String sex;
    private UserExtend userExtend;

    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 Timestamp getBirthday() {return birthday;}

    public void setBirthday(Timestamp birthday) {this.birthday = birthday;}

    public String getSex() {return sex;}

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

    public UserExtend getUserExtend() {return userExtend;}

    public void setUserExtend(UserExtend userExtend) {this.userExtend = userExtend;}
}

UserInfo.java

9159金沙游戏场 99159金沙游戏场 10

/**
 * Created by c-yangx on 11/18/2016.
 */
public class UserExtend {
    private int id;
    private String position;
    private String company;
    private String address;
    private UserInfo userInfo;

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

    public String getPosition() {return position;}

    public void setPosition(String position) {this.position = position;}

    public String getCompany() {return company;}

    public void setCompany(String company) {this.company = company;}

    public String getAddress() {return address;}

    public void setAddress(String address) {this.address = address;}

    public UserInfo getUserInfo() {return userInfo;}

    public void setUserInfo(UserInfo userInfo) {this.userInfo = userInfo;}
}

UserExtend.java

9159金沙游戏场 119159金沙游戏场 12

<?xml version="1.0"  encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="models">
    <class name="models.UserInfo" table="userinfo" catalog="public">
        <id name="id" column="id" type="java.lang.Integer">
            <generator class="native"></generator>
        </id>
        <property name="name" type="java.lang.String">
            <column name="name" length="45" not-null="true">
                <comment>姓名</comment>
            </column>
        </property>
        <property name="birthday" type="java.sql.Timestamp">
            <column name="birthday" length="20" not-null="true">
                <comment>出生日期</comment>
            </column>
        </property>
        <property name="sex" type="java.lang.String">
            <column name="sex" length="5" not-null="true">
                <comment>性别:w=女;m=男;</comment>
            </column>
        </property>
        <many-to-one name="userExtend" column="userExtend" unique="true" not-null="true" cascade="all"></many-to-one>
    </class>
</hibernate-mapping>

UserInfo.hbm.xml

9159金沙游戏场 139159金沙游戏场 14

<?xml version="1.0"  encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="models">
    <class name="models.UserExtend" table="userextend" catalog="public">
        <id name="id" column="id" type="java.lang.Integer">
            <generator class="native"></generator>
        </id>
        <property name="position" type="java.lang.String">
            <column name="position" length="50" not-null="false">
                <comment>职位</comment>
            </column>
        </property>
        <property name="company" type="java.lang.String">
            <column name="company" length="200" not-null="false">
                <comment>公司名</comment>
            </column>
        </property>
        <property name="address" type="java.lang.String">
            <column name="address" length="50" not-null="false">
                <comment>住址</comment>
            </column>
        </property>
        <one-to-one name="userInfo" property-ref="userExtend"></one-to-one>
    </class>
</hibernate-mapping>

UserExtend.hbm.xml

    测试代码为:

@org.junit.Test
    public void save_one2one() throws ParseException {
        //创建对象
        UserInfo userInfo=new UserInfo();
        UserExtend userExtend=new UserExtend();
        userExtend.setPosition("IT");
        userExtend.setCompany("guess");
        userExtend.setAddress("BeiJing CangPing");
        userInfo.setName("andrew");

        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
        userInfo.setBirthday(new Timestamp(simpleDateFormat.parse("1992-01-10").getTime()));

        userInfo.setSex("m");
        userInfo.setUserExtend(userExtend);

        Session session=null;
        Transaction tx=null;
        try{
            session= HibernateUtil.openSession();
            tx=session.beginTransaction();

            session.save(userInfo);

            tx.commit();
        }catch(Exception e){
            tx.rollback();
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            session.close();
        }
    }

    从细分类上,此种属于"唯一外键双向关联",其他的还有"多对一单向关联"、"多对多单向关联"等,这个话题内容较多,就不在此处展开,后面也许会单开一篇文章研究一下.

    对了,最后一定别忘了在主配置文件hibernate.cfg.xml中添加mapping配置。

    放一张数据库截图,生成表结构如图所示:

9159金沙游戏场 15

三层结构是N层结构的一种,一般来说,层次之间是向下依赖的,下层代码未确定其接口(契约)前,上层代码是无法开发的,下层代码接口(契约)的变化将使上层的代码一起变化。

总结

  4.一对多关联添加数据.

    还有一种开发中常见的需求是,表的一对多关心,举个生活中的例子:一个支付宝账号绑定多张银行卡,每张卡都会显示他自己是哪个银行的,卡号是多少...等信息;

    那么我们这里就用两张新表来演示,一张账户表(Account),类比于支付宝账户;一张绑定银行卡表(BindCard),记录例子中的绑定银行卡信息;一个账户会对应有多条绑定银行卡记录.

    a> 两个model类的代码如下:

9159金沙游戏场 169159金沙游戏场 17

/**
 * Created by c-yangx on 11/18/2016.
 */
public class Account {

    private int id;
    private String name;
    private Set<BindCard> bindCards;

    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 Set<BindCard> getBindCards() {return bindCards;}

    public void setBindCards(Set<BindCard> bindCards) {this.bindCards = bindCards;}
}

Account.java

9159金沙游戏场 189159金沙游戏场 19

/**
 * Created by c-yangx on 11/18/2016.
 */
public class BindCard {
    private int id;
    private String cardNum;
    private int cardType;

    private Account account;

    public int getId() {return id;}

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

    public String getCardNum() {return cardNum;}

    public void setCardNum(String cardNum) {this.cardNum = cardNum;}

    public int getCardType() {return cardType;}

    public void setCardType(int cardType) {this.cardType = cardType;}

    public Account getAccount() {return account;}

    public void setAccount(Account account) {this.account = account;}
}

BindCard.java

    b>Account.hbm.xml;

      其中有几点需要特别说明一下:

        1.在映射文件里,通过<set>标签配置一对多的关系映射;

        2.cascade属性设置的是级联操作类型,设置为all时,是所以操作都能执行;  如果只进行"创建"和"修改",也可设置成save-update;

        3.<set>标签的name属性值=持久化类对应的属性名;   <key>的column属性值=与其关联的表的外键;                    

<?xml version="1.0"  encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="models">
    <class name="models.Account" table="account" catalog="public">
        <id name="id" column="id" type="java.lang.Integer">
            <generator class="native"></generator>
        </id>
        <property name="name" type="java.lang.String">
            <column name="name" length="45" not-null="true">
                <comment>账户名</comment>
            </column>
        </property>
        <set name="bindCards" cascade="all" lazy="false">
            <key column="cardId"/>
            <one-to-many class="models.BindCard"/>
        </set>
    </class>
</hibernate-mapping>

    c>最后是测试代码,这就没啥好多说的了

@org.junit.Test
    public void save_one2many(){
        BindCard bindCard1=new BindCard();
        bindCard1.setCardNum("1234343242");
        bindCard1.setCardType(0);

        BindCard bindCard2=new BindCard();
        bindCard2.setCardNum("3421213131");
        bindCard2.setCardType(1);

        Set<BindCard> set=new HashSet<>();
        set.add(bindCard1);
        set.add(bindCard2);

        Account account=new Account();
        account.setName("andrew's account");
        account.setBindCards(set);

        Session session=null;
        Transaction tx=null;
        try{
            session= HibernateUtil.openSession();
            tx=session.beginTransaction();

            session.save(account);

            tx.commit();
        }catch(Exception e){
            tx.rollback();
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            session.close();
        }
    }

      生成的数据表结构:

9159金沙游戏场 20

 

优点: 分工明确,条理清晰,易于调试,而且具有可扩展性。 

本文由澳门游艺场9159发布于澳门游艺场,转载请注明出处:并且可以在delete语句中控制每批删除的数据量

关键词: