Hibernate SQlite mapping class with iner classes exception The database file is locked(数据库被锁定)

Hibernate SQlite mapping class with iner classes exception The database file is locked (database is locked)

我需要用一个内部对象映射对象,该内部对象还有一个内部对象或内部数组。

来自这个回答

{  
   "id":1,
   "zip_code":"0001",
   "user":{  
      "data":{  
         "id":1,
         "username":"user",
         "email":"user@gmail.com"
      }
   }
}

如果我评论对象字段并只留下 zipCode 和 id 一切正常。

Whosebug 说 SQLite 应该有 1 个打开的会话 所以我将 hibernate.connection.pool_size 设置为 1,这样它就会抛出

Java/Hibernate - Exception: The internal connection pool has reached its maximum size and no connection is currently available

但那是昨天。今天又是"locked exception".
我认为是级联问题。当 hibernate 尝试保存第一个 MainEntity 时,它应该保存 User,但是 DB 已经被锁定,结果抛出异常。 即使我是对的,我也不知道如何避免它。我尝试使用 MERGE 级联类型,但它在我的情况下不起作用。

将项目添加到 git 存储库 https://github.com/JoaoMunozIII/hibernate

下面有更多信息。

此后sql查询

Hibernate: select mainentity_.mId, mainentity_.mZipCode as mZipCode2_0_ from main_entity mainentity_ where mainentity_.mId=?
Hibernate: select next_val as id_val from hibernate_sequence
Hibernate: update hibernate_sequence set next_val= ? where next_val=?

我遇到了这个异常 [SQLITE_BUSY] The database file is locked(数据库被锁定)

org.hibernate.exception.LockAcquisitionException: error performing isolated work
    at dialect.SQLiteDialect.convert(SQLiteDialect.java:197)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcIsolationDelegate.delegateWork(JdbcIsolationDelegate.java:79)
    at org.hibernate.id.enhanced.TableStructure.getNextValue(TableStructure.java:125)
    at org.hibernate.id.enhanced.NoopOptimizer.generate(NoopOptimizer.java:40)
    at org.hibernate.id.enhanced.SequenceStyleGenerator.generate(SequenceStyleGenerator.java:412)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:105)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:192)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:177)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:97)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
    at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:651)
    at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:643)
    at org.hibernate.engine.spi.CascadingActions.cascade(CascadingActions.java:218)
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:391)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:316)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:155)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:104)
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:414)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:252)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:182)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:192)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:177)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:97)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
    at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:651)
    at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:643)
    at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:638)
    at DAO.saveEntityDb(DAO.java:16)
    at Main.main(Main.java:32)
Caused by: org.sqlite.SQLiteException: [SQLITE_BUSY]  The database file is locked (database is locked)
    at org.sqlite.core.DB.newSQLException(DB.java:909)
    at org.sqlite.core.DB.newSQLException(DB.java:921)
    at org.sqlite.core.DB.throwex(DB.java:886)
    at org.sqlite.core.DB.exec(DB.java:155)
    at org.sqlite.jdbc3.JDBC3Connection.commit(JDBC3Connection.java:174)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcIsolationDelegate.delegateWork(JdbcIsolationDelegate.java:60)

在项目中。 我创建模型: 主要实体 class

@Entity
@Table(name = "main_entity")
public class MainEntityModel {

    @Id
    private Long mId;

    private Long mZipCode;

    @OneToOne(cascade = CascadeType.ALL, targetEntity = User.class)
    @JoinColumn(name = "id", insertable = false, updatable = false)
    private User mUser;

    public Long getmId() {
        return mId;
    }

    public void setmId(Long mId) {
        this.mId = mId;
    }

    public Long getmZipCode() {
        return mZipCode;
    }

    public void setmZipCode(Long mZipCode) {
        this.mZipCode = mZipCode;
    }

    public User getmUser() {
        return mUser;
    }

    public void setmUser(User mUser) {
        this.mUser = mUser;
    }
}

用户class

@Entity
@Table(name = "user_data")
public class User {

    @OneToOne(cascade = CascadeType.ALL, targetEntity = UserEntity.class)
    @JoinColumn(name="mId")
    private UserEntity mData;

    public UserEntity getData() {
        return mData;
    }

    public void setmData(UserEntity mData) {
        this.mData = mData;
    }

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    public User() {
    }
}

用户实体class

@Entity
@Table(name = "user_entity")
public class UserEntity {

    @Id
    private Long mId;

    private String mUsername;

    public Long getmId() {
        return mId;
    }

    public void setmId(Long mId) {
        this.mId = mId;
    }

    public String getmUsername() {
        return mUsername;
    }

    public void setmUsername(String mUsername) {
        this.mUsername = mUsername;
    }
}

休眠工具

public class Hibernate {

    private static SessionFactory sessionFactory = null;
    private static String dbPath = "D:" + File.separator + "temp.db";

    public static SessionFactory getSessionFactory() {
        System.out.println("factory " + sessionFactory);
        if (sessionFactory == null) {
            Configuration cfg = new Configuration()
                    .setProperty("hibernate.connection.driver_class", "org.sqlite.JDBC")
                    .setProperty("hibernate.dialect", "dialect.SQLiteDialect")
                    .setProperty("hibernate.connection.pool_size", "1")
                    .setProperty("hibernate.connection.url", "jdbc:sqlite:" + dbPath)
                    .setProperty("hibernate.connection.username", "pass")
                    .setProperty("hibernate.connection.password", "pass")
                    .setProperty("hibernate.show_sql", "true")
                    .setProperty("hibernate.format_sql", "false")
                    .setProperty("hibernate.hbm2ddl.auto", "create-drop")
                    .setProperty("hibernate.use_sql_comments", "false")
                    .addAnnotatedClass(MainEntityModel.class)
                    .addAnnotatedClass(User.class)
                    .addAnnotatedClass(UserEntity.class);
            sessionFactory = cfg.buildSessionFactory();
            return sessionFactory;
        } else {
            return sessionFactory;
        }
    }

和主要 class

public class Main {

    static Long userId = 1l;
    private static Long entityId = 1l;
    private static Long entityZip = 1000l;
    private static List<MainEntityModel> mainEntityModelList = new ArrayList();

    public static void main(String[] args) {

        UserEntity userEntity = new UserEntity();
        userEntity.setmId(userId);
        userEntity.setmUsername("User Name " + userId++);

        User user = new User();
        user.setmData(userEntity);

        for (int i = 0; i < 5; i++) {
            MainEntityModel mainEntityModel = new MainEntityModel();
            mainEntityModel.setmId(entityId++);
            mainEntityModel.setmUser(user);
            mainEntityModel.setmZipCode(entityZip++);
            mainEntityModelList.add(mainEntityModel);
        }

        DAO.saveEntityDb(mainEntityModelList);

        System.out.println("saved");

        List<MainEntityModel> savedList = DAO.getEntityDb();

        for (MainEntityModel entity: savedList) {
            System.out.println(entity.getmZipCode() + "\t"
                            + entity.getmUser().getData().getmUsername()
            );
        }
    }
}

DAO class

public class DAO {
    static final Session session = Hibernate.getSessionFactory().openSession();

    public static void saveEntityDb(List<MainEntityModel> entityList){
        Transaction tx=null;
        try {
            tx = session.beginTransaction();
            for (MainEntityModel entity : entityList) {
                session.saveOrUpdate(entity);
            }
            session.flush();
            tx.commit();
        } catch (Exception ex) {
            ex.printStackTrace();
            tx.rollback();
        } finally{
            if(session != null) {
                session.close();
            }
        }
    }

    public static List<MainEntityModel> getEntityDb(){
        Session session = Hibernate.getSessionFactory().openSession();
        List<MainEntityModel> entityModel = session.createQuery("from MainEntityModel").list();
        session.close();
        return entityModel;
    }
}
  1. 问题:user.id 的自动生成 - 它与插入语句相互 locks/concurs。
  2. 问题:锁定后,我们运行在输出循环中进入NPE

解决方案:

  1. 问题

    • 手动分配 user.id 并删除 @GeneratedValue() 注释。 (verified/tested)

      Main.java://or somewhere else
      ...
      user.setId(userId);
      ...
      
      
      User.java:
      ...
      @Id
      //!@GeneratedValue(strategy=GenerationType.AUTO)
      private Long id;
      ...
      
      • "somewhere else" 的一个不错的替代方法是:

        User.java:
        ...
        public void setmData(UserEntity mData) {
            this.mData = mData;
            if(mData == null) {
              this.id = null;
            } else {
              this.id = mData.getmId();
            }
        }
        
        @Id
        //!@GeneratedValue(strategy=GenerationType.AUTO)
        private Long id;
        ...
        
    • 或:在prior/separate事务中保持User/UserData。 ...喜欢...

      • "improve" 你的 DAO.java:

        public static <T extends Object> void saveEntityDb(T... entityList) {
          //local variable!
          final Session session = Hibernate.getSessionFactory().openSession();
          Transaction tx = null;
          try {
            if (session.isConnected()) {
              tx = session.beginTransaction();
              for (T entity : entityList) {
                session.saveOrUpdate(entity);
              }
              session.flush();
              tx.commit();
            }
          } catch (HibernateException ex) {
            if (tx != null && tx.getStatus().canRollback()) {
              tx.rollback();
            }
          } finally {
            if (session != null) {
              session.close();
            }
          }
        }
        
      • 并使用它两次(!):

        Main.java:
        ...
        User user = new User();
        user.setmData(userEntity);
        //do this before...
        DAO.saveEntityDb(user);
        
        List<MainEntityModel> mainEntityModelList = ...
        //...you do this
        DAO.saveEntityDb(mainEntityModelList.toArray(new MainEntityModel[0]));
        
  2. 问题

    • 删除 MainEntityModelJoinColumn 上的 insertable = false, updatable = false

...OneToOne 在这里有点奇怪 (@MainEntityModel) ,它有效,但用作 ManyToOne (5 比 1 ?!)