风人园

弱水三千,只取一瓢,便能解渴;佛法无边,奉行一法,便能得益。
随笔 - 99, 文章 - 181, 评论 - 56, 引用 - 0
数据加载中……

2012年2月22日

Spring boot+Spring Security 4配置整合实例

http://blog.csdn.net/code__code/article/details/53885510

1. 使用Spring Security管理用户身份认证、登录退出

2. 用户密码加密及验证

3. 采用数据库的方式实现Spring Securityremember-me功能

4. 获取登录用户信息。

5.使用Spring Security管理url和权限

posted @ 2018-03-19 21:02 风人园 阅读(303) | 评论 (0)编辑 收藏

spring security 参数配置

     摘要:         // 自定义登录页面          http.csrf().disable().formLogin().loginPage("/login")  //指定登录页的路径&n...  阅读全文

posted @ 2018-03-19 20:33 风人园 阅读(423) | 评论 (0)编辑 收藏

spring quartz 串行配置

<bean id="jobDetail7"class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">

                   <propertyname="targetObject" ref="billingBillTask"></property>

                   <propertyname="targetMethod" value="executeInternal" />

                   <propertyname="concurrent" value="false" />

         </bean>

         <beanid="billingBillTask"class="com.dangdang.tms.job.schedule.bms.BillingBillTask"/>

         <beanid="cronTriggerBean7" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">

                   <propertyname="jobDetail" ref="jobDetail7"></property>

                   <propertyname="cronExpression" value="0/5 * * * * ?"></property>

         </bean>

参考 http://blog.csdn.net/lkforce/article/details/51841890

posted @ 2018-03-12 19:58 风人园 阅读(375) | 评论 (0)编辑 收藏

spring 3.0 async 异步方法 设置

为了解决一些比较费时且不是很紧要的任务,将此任务转为异步任务处理,提高前端操作体验。 spring 中 自带注解 @Async. 配置如下 applicationContext.xml 中 增加 task的引用 如上配置之后,只需要在 需要进行异步调用的方法前面增加 注解就可以了。 @Async public void updateOrderBillItemPQty(String deptId, String orderNo, Integer orderItemSid, Double pQty) { 注:需要注意,同一个对象里面方法调用,不会作为异步方法执行。

posted @ 2017-05-24 14:27 风人园 阅读(251) | 评论 (0)编辑 收藏

android ResourceNotFoundException


在对 TextView 或者 EditText 进行赋值时,调用setText()方法,一定要注意,使用String类型,不要使用int 或者long,否则 会出现找不到资源的异常。系统自动会将int作为一个资源ID,然后去R 里面找,结果找不到。

posted @ 2016-12-21 20:48 风人园 阅读(126) | 评论 (0)编辑 收藏

android 系统提示对话框(AlertDialog)的使用(zt)

     摘要: http://blog.csdn.net/meng425841867/article/details/8523730 在按键单击事件中添加创建对话框并设置相关属性。        [java] view plain copy dialogButton=(Button)findViewBy...  阅读全文

posted @ 2016-12-02 12:54 风人园 阅读(465) | 评论 (0)编辑 收藏

Android 自定义ListView adapter(zt)

     摘要: http://daoshud1.iteye.com/blog/1874241 本文讲实现一个自定义列表的Android程序,程序将实现一个使用自定义的适配器(Adapter)绑定 数据,通过contextView.setTag绑定数据有按钮的ListView。 系统显示列表(ListView)时,首先会实例化一个适配器,本文将实例化一个自定义的适配器。实现 自定义适...  阅读全文

posted @ 2016-12-01 13:13 风人园 阅读(157) | 评论 (0)编辑 收藏

Android之SimpleAdapter简单实例和SimpleAdapter参数说明(zt)

     摘要: http://blog.csdn.net/x605940745/article/details/11981049 SimpleAdapter的参数说明 第一个参数 表示访问整个android应用程序接口,基本上所有的组件都需要 第二个参数表示生成一个Map(String ,Object)列表选项 第三个参数表示界面布局的id  表示该文件作为列表项的组件&...  阅读全文

posted @ 2016-12-01 13:12 风人园 阅读(172) | 评论 (0)编辑 收藏

android开发教程之listview使用方法

首先是布局文件,这里需要两个布局文件,一个是放置列表控件的Activity对应的布局文件 main.xml,另一个是ListView中每一行信息显示所对应的布局 list_item.xml 这一步需要注意的问题是ListView 控件的id要使用Android系统内置的 android:id="@android:id/list"

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width
="match_parent"
    android:layout_height
="match_parent"
    android:orientation
="vertical" >
        
<ListView 
        
android:id="@android:id/list"
        android:layout_width
="match_parent"
        android:layout_height
="match_parent"
        android:padding
="20dip"/>
</LinearLayout>

list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width
="match_parent"
android:layout_height
="match_parent"
android:orientation
="horizontal" >

<TextView
android:id="@+id/user_name"
android:layout_width
="match_parent"
android:layout_height
="match_parent"
android:layout_weight
="1"/>
<TextView
android:id="@+id/user_id"
android:layout_width
="match_parent"
android:layout_height
="match_parent"
android:layout_weight
="1"/>
</LinearLayout>


然后就设置MainActivity中的代码了:基本思想就是先将数据添加到ArrayList中,然后在设置SimpleAdapter适配器完成设置,入下:

package com.example.android_newlistview;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import android.os.Bundle;
import android.app.Activity;
import android.app.ListActivity;
import android.view.Menu;
import android.widget.SimpleAdapter;

public class MainActivity extends ListActivity {


String[] from
={"name","id"}; //这里是ListView显示内容每一列的列名
int[] to={R.id.user_name,R.id.user_id}; //这里是ListView显示每一列对应的list_item中控件的id

String[] userName
={"zhangsan","lisi","wangwu","zhaoliu"}; //这里第一列所要显示的人名
String[] userId={"1001","1002","1003","1004"}; //这里是人名对应的ID

ArrayList
<HashMap<String,String>> list=null;
HashMap
<String,String> map=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//为MainActivity设置主布局
//创建ArrayList对象;
list=new ArrayList<HashMap<String,String>>();
//将数据存放进ArrayList对象中,数据安排的结构是,ListView的一行数据对应一个HashMap对象,
//HashMap对象,以列名作为键,以该列的值作为Value,将各列信息添加进map中,然后再把每一列对应
//的map对象添加到ArrayList中

for(int i=0; i<4; i++){
map
=new HashMap<String,String>(); //为避免产生空指针异常,有几列就创建几个map对象
map.put("id", userId[i]);
map.put(
"name", userName[i]);
list.add(map);
}


//创建一个SimpleAdapter对象
SimpleAdapter adapter=new SimpleAdapter(this,list,R.layout.list_item,from,to);
//调用ListActivity的setListAdapter方法,为ListView设置适配器
setListAdapter(adapter);
}

}


另外对点击某一行作出响应的方法是覆写onListItemClick方法,根据返回的position(从0开始):
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);
}


posted @ 2016-12-01 13:08 风人园 阅读(144) | 评论 (0)编辑 收藏

android json 数据解析



单数据{'singer':{'id':01,'name':'tom','gender':'男'}} 
多个数据{"singers":[ 
        {'id':02,'name':'tom','gender':'男'}, 
         {'id':03,'name':'jerry,'gender':'男'}, 
{'id':04,'name':'jim,'gender':'男'}, 
{'id':05,'name':'lily,'gender':'女'}]}          
// 普通Json数据解析 
    private void parseJson(String strResult) 
        
try 
            JSONObject jsonObj 
= new JSONObject(strResult).getJSONObject("singer"); 
            
int id = jsonObj.getInt("id"); 
            String name 
= jsonObj.getString("name"); 
            String gender 
= jsonObj.getString("gender"); 
            tvJson.setText(
"ID号"+id + ", 姓名:" + name + ",性别:" + gender); 
        }
 catch (JSONException e) 
            System.out.println(
"Json parse error"); 
            e.printStackTrace(); 
        }
 
    }
 
    
//解析多个数据的Json
   private void parseJsonMulti(String strResult) 
       
try 
            JSONArray jsonObjs 
= new JSONObject(strResult).getJSONArray("singers"); 
            String s 
= ""
            
for(int i = 0; i < jsonObjs.length() ; i++)
                JSONObject jsonObj 
= ((JSONObject)jsonObjs.opt(i)).getJSONObject("singer"); 
                
int id = jsonObj.getInt("id"); 
                String name 
= jsonObj.getString("name"); 
                String gender 
= jsonObj.getString("gender"); 
                s 
+=  "ID号"+id + ", 姓名:" + name + ",性别:" + gender+ "\n" ; 
            }
 
            tvJson.setText(s); 
        }
 catch (JSONException e) 
            System.out.println(
"Jsons parse error !"); 
            e.printStackTrace(); 
        }
 
    }
 

posted @ 2016-11-29 15:09 风人园 阅读(151) | 评论 (0)编辑 收藏

button onClick 事件的几种调用方式



  1. //第一种方式    
  2.         Button Btn1 = (Button)findViewById(R.id.button1);//获取按钮资源    
  3.         Btn1.setOnClickListener(new Button.OnClickListener(){//创建监听    
  4.             public void onClick(View v) {    
  5.                 String strTmp = "点击Button01";    
  6.                 Ev1.setText(strTmp);    
  7.             }    
  8.   
  9.         });   

  1.     //第二种方式(Android1.6版本及以后的版本中提供了,直接在layout中设置)    
  2.     public void Btn3OnClick(View view){    
  3.         String strTmp="点击Button03";  
  4.         Ev1.setText(strTmp);  
  5.   
  6.     }  

  1.  <Button  
  2.         android:id="@+id/button3"  
  3.         android:layout_width="wrap_content"  
  4.         android:layout_height="wrap_content"  
  5.         android:text="Button3"   
  6.         android:onClick="Btn3OnClick"/>  


第三种方式 activity 实现 单击监听接口

public class TestButtonActivity extends Activity implements OnClickListener {
Button btn1, btn2;
Toast tst;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_button);
btn1 = (Button) findViewById(R.id.button1);
btn2 = (Button) findViewById(R.id.button2);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.button1:
tst = Toast.makeText(this, "111111111", Toast.LENGTH_SHORT);
tst.show();
break;
case R.id.button2:
tst = Toast.makeText(this, "222222222", Toast.LENGTH_SHORT);
tst.show();
break;
default:
break;
}
}
}




 

posted @ 2016-11-29 13:31 风人园 阅读(896) | 评论 (0)编辑 收藏

打开activity的几种方式


一、直接打开,不传递参数
Intent intent = new Intent(this, Activity.class);
startActivity(intent);


二、传递参数
public void OpenNew(View v) {
    
//新建一个显式意图,第一个参数为当前Activity类对象,第二个参数为你要打开的Activity类
    Intent intent =new Intent(MainActivity.this,MainActivity2.class);
    
    
//用Bundle携带数据
    Bundle bundle=new Bundle();
    
//传递name参数为tinyphp
    bundle.putString("name""tinyphp");
    intent.putExtras(bundle);
    
    startActivity(intent);       
 //1.要关闭的页面  
    protected void onCreate(Bundle savedInstanceState) {  
        
super.onCreate(savedInstanceState);  
        setContentView(R.layout.otheractivity);  
        Intent intent 
= this.getIntent();  
        intent.putExtra(
"tel"12345);  
        
//设置requestCode和带有数据的intent对象  
        OtherActivity.this.setResult(3, intent);  
        
//马上关闭Activity  
        this.finish();  
    }
  
      
    
//2.上面的页面关闭时,此页面进行数据的接收  
    class ButtonListener implements android.view.View.OnClickListener{  
        @Override  
        
public void onClick(View arg0) {  
            Intent intent 
= new Intent();  
            intent.setClass(MainActivity.
this, OtherActivity.class);  
            
//与普通的start方法不同,需要设置requestCode  
            startActivityForResult(intent, 1);  
        }
  
    }
  
      
    
//如果要进行此操作,需要在数据接收页面中复写activity的onActivityResul()方法  
    @Override  
    
protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
        
super.onActivityResult(requestCode, resultCode, data);  
        
int tel = 0;  
        
//根据返回码resultCode来判断下一步进行的业务代码  
        if(resultCode==3){  
            tel 
= data.getIntExtra("tel"0);  
        }
  
        Log.i(TAG, 
"tel--------->"+String.valueOf(tel));  
    }
  
 
    }



三、回传参数


posted @ 2016-11-29 13:24 风人园 阅读(435) | 评论 (0)编辑 收藏

spring mvc @ExceptionHandler 异常处理

spring mvc 统一的异常处理,有两种方式。
一、exceptionResolver
二、ExceptionHandler

两者不能同时配置。如果配置了第一种,则第二种无效。就因为这个原因,找了一天的问题。

ExceptionHandler 通过注解的方式,进行配置,只需要在某个controller 中设置了这个注解,则这个controller中的所有异常都会通过这个方法进行处理。

比如rest controller,增加一个
    @ExceptionHandler
    
public String exp(HttpServletRequest request, Exception ex) {  
        
        Map map 
= new HashMap();
        
        logger.error(
"error," + UUID.randomUUID().toString(), ex);
        request.setAttribute(
"ex", ex);  
        
        String msg 
= ex.getMessage();
        
        map.put(
"success""0"); 
        map.put(
"msg", msg);
        
        String rtnjson 
= JSONUtil.objectToJson(map);
        
        request.setAttribute(
"json", rtnjson);
        
        
return "json";
    }
 

这样,这个controler中的异常,都会由这个异常处理方法,进行统一处理,生成异常json。避免在各个方法中通过try catch 的方法,进行异常处理。

posted @ 2016-11-08 08:32 风人园 阅读(384) | 评论 (0)编辑 收藏

有关使用 ActionBarSherlock 自定义样式的问题

1、不同android 版本,需要设置不同的 sytle 文件。
    默认 通常我们会设置values下面的style 文件
    但是如果是4.0以上,则还需要设置values-v14下面的style文件。
    
否则不能生效

posted @ 2014-03-30 09:00 风人园 阅读(460) | 评论 (0)编辑 收藏

解决IIS 启动响应超时 或者 World Wide Web Publishing服务无法启动提示:错误127 找不到指定程序/路径

找了很多方案,都说是那个KB939373 这个补丁搞的,但是在添加删除里就是找不到这个东东,重装也没有用,后来找到了这篇文章
http://www.cnblogs.com/skylaugh/archive/2011/07/21/2112860.html

根据这篇文章,我把 infocomm.dll 给替换一下,然后就好了,简直就是坑爹啊。

这里讲讲需要注意的事项。
1、替换的文件通过iis安装文件生成,请看参考文章
2、文件直接黏贴覆盖,不要使用参考里面bat处理。
3、然后启动这个服务,启动iis,搞定

posted @ 2013-06-18 15:49 风人园 阅读(224) | 评论 (0)编辑 收藏

解决Tomcat Error listenerStart 问题

昨天部署web应用到Tomcat之后,无法成功启动,并且控制台没有详细的错误信息,
顶多就两行提示信息,例如:
严重: Error listenerStart
严重: Context [/] startup failed due to previous errors

或者

严重: Error filterStart
org.apache.catalina.core.StandardContext start
严重: Context startup failed due to previous errors

查找logs目录下的信息,除了这两句话,也没别的辅助内容.
给查错带来了困难,在这种情况下,是因为Tomcat自身的默认日志没有将一些错误信息输出到控制台或文件,
这个时候则需要配置Tomcat自身的log,启用详细的调试日志.

3.log4j配置文件:
log4j.properties
配置内容为:

log4j.rootLogger=ERROR,R

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%p]%t-%c-%m%n

log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=${catalina.home}/logs/tomcat.log
log4j.appender.R.MaxFileSize=10MB
log4j.appender.R.MaxBackupIndex=10
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=[%p]%t-%c-%m%n

log4j.logger.org.apache.catalina=INFO,R,CONSOLE

#日志级别不能太低,如果配置为debug的话,输出的日志信息太多,导致tomcat启动非常的慢.

4.Tomcat 6.0所需的juli替换文件:
http://www.apache.org/dist/--escaped_anchor:079305423cce36d6691457475e081123--/tomcat-6/v6.0.18/bin/extras/tomcat-juli-adapters.jar
http://www.apache.org/dist/--escaped_anchor:079305423cce36d6691457475e081123--/tomcat-6/v6.0.18/bin/extras/tomcat-juli.jar
以上两个链接基本没用,大家可以搜索一下,csdn上面有人提供jar包下载,这里附件也不能加,没有办法。

在Tomcat6.0中,

将tomcat-juli-adapters.jar,log4j-1.2.15.jar,log4j.properties复制到D:\Java\ApacheTomcat6.0.14\lib下面.

将tomcat-juli.jar复制到D:\Java\apache-tomcat-6.0.14\bin\下面.

然后启动tomcat,就可以在D:\Java\apache-tomcat-6.0.14\logs下看到tomcat.log了.

在这个时候,再通过日志文件来分析,则会发现出现这种错误的情况可能有:
(以下是我遇到的出错情况,大多是些低级错误)
1.webapps要用到的classe文件根本没有复制到WEB-INF/classes目录下面
(java.lang.NoClassDefFoundError,而这个信息可能默认没输出到控制台,尤其是用了spring的,昨天就是这个粗心的低级错误)
2.要用到lib文件没有复制完,缺少lib
3.lib下的同一个库的jar文件存在多个不同版本,引起版本冲突.
4.lib下的jar与tomcat版本不相对应(我遇到的问题是web应用在Tomcat5.5上运行正常,换到Tomcat6.0上就出错,
例如一个用了struts的webapp在Tomcat 6上报下面的错误
“Parse Fatal Error at line 17 column 6: The processing instruction
target matching “[xX][mM][lL]” is not allowed” )

愿意看英文的可以参考官方网站说明:
http://tomcat.apache.org/tomcat-6.0-doc/logging.html


标记一下:) 

posted @ 2012-04-20 15:39 风人园 阅读(2867) | 评论 (0)编辑 收藏

javax.servlet 和 javax.servlet.jsp导致系统错误的问题

提示无法载入 c.tld等信息

只要删除 WEB-INF/lib 下的上述两个文件即可

因为此文件与 tomcat中的文件版本冲突

posted @ 2012-04-20 14:16 风人园 阅读(233) | 评论 (0)编辑 收藏

[转]浅谈Hibernate的flush机制

浅谈Hibernate的flush机制

随着Hibernate在Java开发中的广泛应用,我们在使用Hibernate进行对象持久化操作中也遇到了各种各样的问题。这些问题往往都是我们对Hibernate缺乏了解所致,这里我讲个我从前遇到的问题及一些想法,希望能给大家一点借鉴。

      这是在一次事务提交时遇到的异常。
      an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
net.sf.hibernate.AssertionFailure: possible nonthreadsafe access to session
注:非possible non-threadsafe access to the session (那是另外的错误,类似但不一样)
      这个异常应该很多的朋友都遇到过,原因可能各不相同。但所有的异常都应该是在flush或者事务提交的过程中发生的。这一般由我们在事务开始至事务提交的 过程中进行了不正确的操作导致,也会在多线程同时操作一个Session时发生,这里我们仅仅讨论单线程的情况,多线程除了线程同步外基本与此相同。
      至于具体是什么样的错误操作那?我给大家看一个例子(假设Hibernate配置正确,为保持代码简洁,不引入包及处理任何异常)
SessionFactory sf = new Configuration().configure().buildSessionFactory() ;
Session s = sf.openSession();
Cat cat = new Cat();
Transaction tran = s.beginTransaction(); (1)
s.save(cat); (2)(此处同样可以为update delete)
s.evict(cat); (3)
tran.commit(); (4)
s.close();(5)
      这就是引起此异常的典型错误。我当时就遇到了这个异常,检查代码时根本没感觉到这段代码出了问题,想当然的认为在Session上开始一个事务,通过 Session将对象存入数据库,再将这个对象从Session上拆离,提交事务,这是一个很正常的流程。如果这里正常的话,那问题一定在别处。
        问题恰恰就在这里,我的想法也许没有错,但是一个错误的论据所引出的观点永远都不可能是正确的。因为我一直以为直接在对数据库进行操作,忘记了在我与数据 库之间隔了一个Hibernate,Hibernate在为我们提供持久化服务的同时,也改变了我们对数据库的操作方式,这种方式与我们直接的数据库操作 有着很多的不同,正因为我们对这种方式没有一个大致的了解造成了我们的应用并未得到预先设想的结果。
那Hibernate的持久化机制到底有什么不同那?简单的说,Hibernate在数据库层之上实现了一个缓存区,当应用save或者update一个 对象时,Hibernate并未将这个对象实际的写入数据库中,而仅仅是在缓存中根据应用的行为做了登记,在真正需要将缓存中的数据flush入数据库时 才执行先前登记的所有行为。
在实际执行的过程中,每个Session是通过几个映射和集合来维护所有与该Session建立了关联的对象以及应用对这些对象所进行的操作的,与我们这 次讨论有关的有entityEntries(与Session相关联的对象的映射)、insertions(所有的插入操作集合)、 deletions(删除操作集合)、updates(更新操作集合)。下面我就开始解释在最开始的例子中,Hibernate到底是怎样运作的。
(1)生成一个事务的对象,并标记当前的Session处于事务状态(注:此时并未启动数据库级事务)。
(2)应用使用s.save保存cat对象,这个时候Session将cat这个对象放入entityEntries,用来标记cat已经和当前的会话建 立了关联,由于应用对cat做了保存的操作,Session还要在insertions中登记应用的这个插入行为(行为包括:对象引用、对象id、 Session、持久化处理类)。
(3)s.evict(cat)将cat对象从s会话中拆离,这时s会从entityEntries中将cat这个对象移出。
(4)事务提交,需要将所有缓存flush入数据库,Session启动一个事务,并按照insert,update,……,delete的顺序提交所有 之前登记的操作(注意:所有insert执行完毕后才会执行update,这里的特殊处理也可能会将你的程序搞得一团糟,如需要控制操作的执行顺序,要善 于使用flush),现在cat不在entityEntries中,但在执行insert的行为时只需要访问insertions就足够了,所以此时不会 有任何的异常。异常出现在插入后通知Session该对象已经插入完毕这个步骤上,这个步骤中需要将entityEntries中cat的 existsInDatabase标志置为true,由于cat并不存在于entityEntries中,此时Hibernate就认为 insertions和entityEntries可能因为线程安全的问题产生了不同步(也不知道Hibernate的开发者是否考虑到例子中的处理方 式,如果没有的话,这也许算是一个bug吧),于是一个net.sf.hibernate.AssertionFailure就被抛出,程序终止。

我想现在大家应该明白例子中的程序到底哪里有问题了吧,我们的错误的认为s.save会立即的执行,而将cat对象过早的与Session拆离,造成了 Session的insertions和entityEntries中内容的不同步。所以我们在做此类操作时一定要清楚Hibernate什么时候会将数 据flush入数据库,在未flush之前不要将已进行操作的对象从Session上拆离。

对于这个错误的解决方法是,我们可以在(2)和(3)之间插入一个s.flush()强制Session将缓存中的数据flush入数据库(此时 Hibernate会提前启动事务,将(2)中的save登记的insert语句登记在数据库事务中,并将所有操作集合清空),这样在(4)事务提交时 insertions集合就已经是空的了,即使我们拆离了cat也不会有任何的异常了。
前面简单的介绍了一下Hibernate的flush机制和对我们程序可能带来的影响以及相应的解决方法,Hibernate的缓存机制还会在其他的方面给我们的程序带来一些意想不到的影响。看下面的例子:
(name为cat表的主键)
Cat cat = new Cat();
cat.setName(“tom”);
s.save(cat);
cat.setName(“mary”);
s.update(cat);(6)
Cat littleCat = new Cat();
littleCat.setName(“tom”);
s.save(littleCat);
s.flush();
这个例子看起来有什么问题?估计不了解Hibernate缓存机制的人多半会说没有问题,但它也一样不能按照我们的思路正常运行,在flush过程中会产 生主键冲突,可能你想问:“在save(littleCat)之前不是已经更改cat.name并已经更新了么?为什么还会发生主键冲突那?”这里的原因 就是我在解释第一个例子时所提到的缓存flush顺序的问题,Hibernate按照insert,update,……,delete的顺序提交所有登记 的操作,所以你的s.update(cat)虽然在程序中出现在s.save(littleCat)之前,但是在flush的过程中,所有的save都将 在update之前执行,这就造成了主键冲突的发生。
这个例子中的更改方法一样是在(6)之后加入s.flush()强制Session在保存littleCat之前更新cat的name。这样在第二次 flush时就只会执行s.save(littleCat)这次登记的动作,这样就不会出现主键冲突的状况。再看一个例子(很奇怪的例子,但是能够说明问 题)
Cat cat = new Cat();
cat.setName(“tom”);
s.save(cat); (7)
s.delete(cat);(8)
cat.id=null;(9)
s.save(cat);(10)
s.flush();
这个例子在运行时会产生异常net.sf.hibernate.HibernateException: identifier of an instance of Cat altered from 8b818e920a86f038010a86f03a9d0001 to null这里例子也是有关于缓存的问题,但是原因稍有不同:(7)和(2)的处理相同。(8)Session会在deletions中登记这个删除动作,同时更新entityEntries中该对象的登记状态为DELETED。(9)Cat类的标识符字段为id,将其置为null便于重新分配id并保存进数据库。
(10)此时Session会首先在entityEntries查找cat对象是否曾经与Session做过关联,因为cat只改变了属性值,引用并未改 变,所以会取得状态为DELETED的那个登记对象。由于第二次保存的对象已经在当前Session中删除,save会强制Session将缓存 flush才会继续,flush的过程中首先要执行最开始的save动作,在这个save中检查了cat这个对象的id是否与原来执行动作时的id相同。 不幸的是,此时cat的id被赋为null,异常被抛出,程序终止(此处要注意,我们在以后的开发过程尽量不要在flush之前改变已经进行了操作的对象 的id)。
这个例子中的错误也是由于缓存的延时更新造成的(当然,与不正规的使用Hibernate也有关系),处理方法有两种:
1、在(8)之后flush,这样就可以保证(10)处save将cat作为一个全新的对象进行保存。
2、删除(9),这样第二次save所引起的强制flush可以正常的执行,在数据库中插入cat对象后将其删除,然后继续第二次save重新插入cat对象,此时cat的id仍与从前一致。
这两种方法可以根据不同的需要来使用,呵呵,总觉得好像是很不正规的方式来解决问题,但是问题本身也不够正规,只希望能够在应用开发中给大家一些帮助,不对的地方也希望各位给与指正。
  总的来说,由于Hibernate的flush处理机制,我们在一些复杂的对象更新和保存的过程中就要考虑数据库操作顺序的改变以及延时flush是 否对程序的结果有影响。如果确实存在着影响,那就可以在需要保持这种操作顺序的位置加入flush强制Hibernate将缓存中记录的操作flush入 数据库,这样看起来也许不太美观,但很有效。

http://hi.baidu.com/lkdlhw_2000/blog/item/a35b9cca82945342f31fe769.html

posted @ 2012-02-22 13:11 风人园| 编辑 收藏