2

私はこの問題を抱えています:

エンティティでビューを準備します

@RequestMapping("/admin/modifyImplant/{id}")
 public ModelAndView modifyImplant(@PathVariable Integer id) {
  ModelAndView mav = new ModelAndView();

  Implant currentImplant =impDAO.findById(id); //this is the dao
  Implanttype it= currentImplant.getImplanttype();
  Implantinverter ii=new Implantinverter();
  ArrayList<Implantpanel> implantpanels = new ArrayList<Implantpanel>();
  try{
      ii= (Implantinverter)currentImplant.getImplantinverters().toArray()[0];
      implantpanels=(ArrayList<Implantpanel>) imppanelDAO.findByProperty("implant",currentImplant );
  }
  catch (Exception ex){
      //TODO
  }
  mav.addObject("implantpanels", implantpanels);
  mav.addObject("implantinverter",ii);
  mav.addObject("implant", currentImplant); //THIS IS THE ENTITY I MEAN
  mav.addObject("implanttype",it);
  mav.setViewName("/implant/modifyImplant.jsp");

  return mav;


}

jsp (コードの一部) へのこのコード アプローチ

<form:form action="${pageContext.request.contextPath}/admin/finalizeModifyImplant/${implant.id}" method="POST" modelAttribute="implant">
        <table cellpadding="0" cellspacing="0" id="viewTable" >
            <tbody>
                <tr>
                <td class="label">
                    Name:
                </td>   
                <td>    
                    <form:input  id="User_namesurname" path="businessname" cssStyle="width:300px;"/>
                </td>
                </tr>

エンティティのフィールドについても同様です。エンティティには、外部キーで接続されたいくつかのエンティティが関連付けられています (implanttype、implantinverter、implantpanels など)。

提出物はこのコントローラーに行きます:

 @RequestMapping(value = "/admin/finalizeModifyImplant/{id}",
method = { RequestMethod.GET,RequestMethod.POST })
public ModelAndView finalizeModifyImplant(@PathVariable int id, @Valid @ModelAttribute Implant implant, BindingResult result){
    ModelAndView mav=new ModelAndView();
    mav.setViewName("../../admin/modifyImplant/"+id);
    if (result.hasErrors()){
        return mav;
    }

    implant.setId(id); //NOTICE WHAT I NEED TO DO HERE!
    Implant oldImplant= impDAO.findById(id);
    implant.setImplantinverters(oldImplant.getImplantinverters());
    implant.setImplantpanels(oldImplant.getImplantpanels());
    implant.setImplanttype(oldImplant.getImplanttype());
    implant.setInverters(oldImplant.getInverters());
    implant.setPvgis(oldImplant.getPvgis());
    try{
        impDAO.merge(implant); //here i call getSession().merge()
    }catch(RuntimeException re){
        return mav;
        //TODO: errors
    }

フォームの送信を取得すると (これは更新です)、次の問題があります。

以下はインプラントエンティティです

public class Implant implements java.io.Serializable {
// Fields
private Integer id;
private Pvgis pvgis;
private Gateway gateway;
private Implanttype implanttype;
@JsonIgnore //I NEED TO JSONIGNORE IT BECAUSE IT HAS A RECURSIVE CALL ON IMPLANT
private Users users;
@NotEmpty
private String nameimplant;
private String place;
private double latitude;
private double longitude;
private double expectedpower;
private double tolerance;
[...] and many other fields

および関連するimplant.hbm.xml

<hibernate-mapping>
<class name="it.pstmarche.model.Implant" table="implant" schema="public">
    <id name="id" type="integer">
        <column name="id" />
        <generator class="identity" />
    </id>
    <many-to-one name="pvgis" class="it.pstmarche.model.Pvgis" fetch="select">
        <column name="idpvgis" />
    </many-to-one>
    <many-to-one name="gateway" class="it.pstmarche.model.Gateway" fetch="select">
        <column name="idgateway" />
    </many-to-one>
    <many-to-one name="implanttype" class="it.pstmarche.model.Implanttype" fetch="select">
        <column name="implanttype" />
    </many-to-one>
    <many-to-one name="users" class="it.pstmarche.model.Users" fetch="select" >
        <column name="iduser" />
    </many-to-one>
    <property name="nameimplant" type="string">
        <column name="nameimplant" not-null="true">
            <comment>Name</comment>
        </column>

データベースから更新されたエンティティを取得しようとすると(findByIdまたは直接クエリを介して)ランダムな結果が得られるため、更新を正しく行う方法が本当にわかりません。正しい更新されたエンティティを取得することもあれば、古いものを取得することもあります実在物!!本当にランダム。

私が試した: 1. キャッシュを削除する ( session.setCacheMode(CacheMode.IGNORE); ) 結果はありません 2. getSession().flush(); を追加します。getSession().close(); merge メソッドの最後で、次のことを行います。

public void merge(Implant currentImp){
    //Session session = HibernateSessionFactory.getSessionFactory().openSession();
    Transaction tx= getSession().beginTransaction();
    if (currentImp.getId() != null) { // it is an update
        getSession().merge(currentImp);
        tx.commit();
        //getSession().update(currentImp);
        log.debug("MERGE");
        getSession().flush();// THAT'S THE ADD
        session.close(); //THIS IS ALSO
    } else { // you are saving a new one
        getSession().saveOrUpdate(currentImp);
        tx.commit();
        getSession().flush();
        getSession().close();
        log.debug("Save sou");
    }

Hibernate + Spring を使用して更新を実装する正しい方法を理解できません! どんな助けでも大歓迎です。ありがとう

編集:私の質問が明確ではなかったのかもしれません。現在の問題は、エンティティをランダムに保存またはマージした後、(クエリ findById(int id) のように) エンティティを検索すると、古いエンティティがランダムに取得されることです (まだ Hibernate セッションにあると思います)。この問題を無効にする唯一の方法は、セッションをクリアすること(getSession().clear()) ですが、この後明らかに Lazy 例外が発生します -必要なエンティティの関連エンティティを取得しようとすると、プロキシはありません。セッション!残念です

2EDIT:Tim Hに感謝します。ただし、休止状態セッションの処理に関連する問題にまだ直面しています。序文:私はスレッドごとに休止状態のセッションを使用します

private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
private  static Configuration configuration = new Configuration();    
private static org.hibernate.SessionFactory sessionFactory;
private static String configFile = CONFIG_FILE_LOCATION;
static {
    try {
        configuration.configure(configFile);
        sessionFactory = configuration.buildSessionFactory();
    } catch (Exception e) {
        System.err
                .println("%%%% Error Creating SessionFactory %%%%");
        e.printStackTrace();
    }
}
private HibernateSessionFactory() {
}
public static Session getSession() throws HibernateException {
    Session session = (Session) threadLocal.get();
    if (session == null || !session.isOpen()) {
        if (sessionFactory == null) {
            rebuildSessionFactory();    
        }
        session = (sessionFactory != null) ? sessionFactory.openSession()
                : null;
        threadLocal.set(session);
    }
    return session;
}

ここで、Tomcat スレッド 1 でデータベースからエンティティ x をロードするとします。エンティティ値は100です。セッションは遅延初期化されているため、ロード後にセッションを閉じることができません(また、セッションを閉じると、遅延初期化例外 (プロキシ/セッションなし) が発生するリスクがあります)。

その後、スレッド 2 でエンティティ x の値を更新します。現在の値は 50です。

最後に、値をロードし直しましたが、Tomcat はエンティティがまだスレッドのセッションにあり、値がまだ 100 であるスレッド 1 に移動しました。新しい値ではなく古い値を取得します!!

したがって、jsp が終了するまでセッションを閉じることはできません (その上で遅延フィールドを使用します)。次の方法で jsp の最後にセッションを閉じるには、このアンチパターンを実行する必要があります: <% HibernateSessionFactory.getSession().close (); %> そしてそれは恐ろしいことです! ビューとモデルを結合します! これを安全に行うためのパターンはありますか??

4

2 に答える 2

1

あなたのお悩みを少しでも解決できるかもしれません。

  1. リクエスト パラメータがエンティティの id プロパティと一致する場合 (つまり、非表示のフォーム フィールド) にのみ、ID がエンティティにバインドされます。

  2. バインディングの残りの部分では、"@ModelAttribute" トリックと呼ばれるものを使用できます。基本的に、 @ModelAttribute でアノテーションが付けられたメソッドはすべてのリクエストの前に呼び出されるため、このメソッドはエンティティを返し、Spring はフォームからバッキング オブジェクトをフォーム バインドします。

    @ModelAttribute インプラント インプラント public Implant getBackingImplant(HttpServletRequest request) { if(request.getParameter("id") != null return dao.getImplantByIdWithAllMyRelationshipsFilledIn(request.getParameter("id"));

    新しいインプラント()を返します。}

これで、Spring は getBackingImplant によって返されたエンティティに対してフォームをバインドします。

あなたの保存が機能していない限り、私は休止状態の人ではないことを本当に確信していませんが、上記はあなたのフォームバインディングの問題を解決するはずです.

于 2012-04-19T00:53:28.220 に答える
0

問題がより明確になったため、新しい回答を追加します。merge は、マージしたエンティティを返すことを忘れないでください。merge コマンドの結果を破棄しているので、代わりにそれをコントローラーに返す必要があります。

于 2012-04-19T11:45:31.277 に答える