【问题标题】:How to fix the Hibernate "object references an unsaved transient instance - save the transient instance before flushing" error如何修复 Hibernate“对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例”错误
【发布时间】:2011-01-19 03:44:58
【问题描述】:

当我使用 Hibernate 保存对象时收到以下错误

object references an unsaved transient instance - save the transient instance before flushing

【问题讨论】:

  • 在想要的上下文中是这个错误吗?是有还是没有瞬态变量?
  • 如果您尝试保存的对象之间存在关系,请确保它们以正确的顺序保存。例如:作者在书之前被保存,因为作者是这本书的必备品。同样明智。

标签: java hibernate jpa orm entity


【解决方案1】:

简介

使用 JPA 和 Hibernate 时,实体可以处于以下 4 种状态之一:

  • New - 从未与 Hibernate Session(又名 Persistence Context)关联且未映射到任何数据库表行的新创建对象被视为处于 New 或 Transient 状态。

要成为持久化,我们需要显式调用persist 方法或使用传递持久化机制。

  • 持久性 - 持久性实体已与数据库表行相关联,并由当前运行的持久性上下文管理。

将检测到对此类实体所做的任何更改并将其传播到数据库(在会话刷新期间)。

  • 已分离 - 一旦当前运行的持久性上下文关闭,所有以前管理的实体都将分离。将不再跟踪连续的更改,也不会发生自动数据库同步。

  • 已删除 - 尽管 JPA 要求只允许删除托管实体,但 Hibernate 也可以删除分离的实体(但只能通过 remove 方法调用)。

实体状态转换

要将实体从一种状态移动到另一种状态,您可以使用persistremovemerge 方法。

解决问题

您在问题中描述的问题:

object references an unsaved transient instance - save the transient instance before flushing

是由于将处于 New 状态的实体与处于 Managed 状态的实体相关联。

当您将子实体与父实体中的一对多集合相关联,并且该集合没有cascade 实体状态转换时,可能会发生这种情况。

因此,您可以通过向触发此故障的实体关联添加级联来解决此问题,如下所示:

@OneToOne 关联

@OneToOne(
    mappedBy = "post",
    orphanRemoval = true,
    cascade = CascadeType.ALL)
private PostDetails details;

注意我们为cascade 属性添加的CascadeType.ALL 值。

@OneToMany 关联

@OneToMany(
    mappedBy = "post", 
    orphanRemoval = true,
    cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();

同样,CascadeType.ALL 适用于双向 @OneToMany 关联。

现在,为了使级联在双向正常工作,您还需要确保父关联和子关联同步。

@ManyToMany 关联

@ManyToMany(
    mappedBy = "authors",
    cascade = {
        CascadeType.PERSIST, 
        CascadeType.MERGE
    }
)
private List<Book> books = new ArrayList<>();

@ManyToMany 关联中,您不能使用CascadeType.ALLorphanRemoval,因为这会将删除实体状态转换从一个父实体传播到另一个父实体。

因此,对于@ManyToMany 关联,您通常级联CascadeType.PERSISTCascadeType.MERGE 操作。或者,您可以将其扩展为 DETACHREFRESH

【讨论】:

  • Hibernate 如何判断实例是否分离?它只是查看 ID,如果它为 null,它认为实例已分离还是涉及其他内容?
  • 对于合并,它运行一个 SELECT 查询。否则,它会检查 id 和版本。
【解决方案2】:

当您有 OneToMany 关系并且您尝试将子实体添加到父实体中的列表时,也会发生这种情况,然后通过父实体检索此列表(在保存此父实体之前), 不保存子实体本身,例如:

Child childEntity = new Child();
parentEntity.addChild(childEntity);
parentEntity.getChildren(); // I needed the retrieval for logging, but one may need it for other reasons.
parentRepository.save(parentEntity);

当我保存父实体时抛出错误。如果我删除了上一行的检索,则不会抛出错误,但这当然不是解决方案。

解决方案是保存 childEntity 并将 添加 保存的子实体到父实体,如下所示:

Child childEntity = new Child();
Child savedChildEntity = childRepository.save(childEntity);
parentEntity.addChild(savedChildEntity);
parentEntity.getChildren();
parentRepository.save(parentEntity);

【讨论】:

    【解决方案3】:

    在我使用 Cascade.ALL 的情况下,向表中添加了不必要的条目,该表已经具有相同的对象(生成没有 id 的子值就是这样做的)。 因此,我必须先从存储库中获取子对象,然后将其设置到主对象中,而不是导致问题的对象。

    Student s = studentRepo.findByName(generatedObject.getStudent().getName())
    generatedObject.student(s);   
    

    【讨论】:

      【解决方案4】:

      在引入乐观锁定 (@Version) 后,我在所有 PUT HTTP 事务中都面临同样的错误

      在更新实体时,必须发送该实体的 ID 和版本。如果任何实体字段与其他实体相关,那么对于该字段,我们还应该提供 id 和版本值,而不是 JPA 尝试首先将该相关实体作为新实体持久化

      示例:我们有两个实体 --> Vehicle(id,Car,version) ; 汽车(id、版本、品牌);更新/保留车辆实体确保车辆实体中的 Car 字段提供了 id 和 version 字段

      【讨论】:

        【解决方案5】:

        就我而言,问题完全不同。我有两个课程,比如说 c1 和 c2。 C1 和 C2 之间的依赖是 OneToMany。现在,如果我将 C1 保存在 DB 中,则会引发上述错误。

        这个问题的解决方法是从消费者请求中获取第一个 C2 的 id 并通过存储库调用找到 C2。然后将 c2 保存到 C1 对象中。现在如果我保存 C1,它工作正常。

        【讨论】:

          【解决方案6】:

          只需在您的基类中创建您的映射的构造函数。 就像如果您想要实体 A、实体 B 中的一对一关系。 如果您将 A 作为基类,那么 A 必须有一个 Constructor 有 B 作为参数。

          【讨论】:

            【解决方案7】:

            我的问题与 JUnit 的 @BeforeEach 有关。即使我保存了相关实体(在我的情况下为 @ManyToOne),我也得到了同样的错误。

            这个问题在某种程度上与我父母的顺序有关。 如果我将值分配给该属性,问题就解决了。

            例如。 如果我有可以有一些类别(一个或多个)的实体 Question 并且实体 Question 有一个序列:

            @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")
            @Id
            private Long id;
            

            我必须分配值question.setId(1L);

            【讨论】:

              【解决方案8】:

              当我在标记为@Transactional 的方法中创建一个新实体和一个关联实体,然后在保存之前执行查询时,我发生了这个问题。例

              @Transactional
              public someService() {
                  Entity someEntity = new Entity();
                  AssocaiatedEntity associatedEntity = new AssocaitedEntity();
                  someEntity.setAssociatedEntity(associatedEntity);
                  associatedEntity.setEntity(someEntity);
              
                  // Performing any query was causing hibernate to attempt to persist the new entity. It would then throw an exception
                  someDao.getSomething();
              
                  entityDao.create(someEntity);
              }
              

              为了解决这个问题,我在创建新实体之前执行了查询。

              【讨论】:

                【解决方案9】:

                我也面临同样的情况。通过在属性上方设置以下注解使其解决了提示的异常。

                我遇到的异常。

                Exception in thread "main" java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.model.Car_OneToMany
                

                为了克服,我使用的注释。

                    @OneToMany(cascade = {CascadeType.ALL})
                    @Column(name = "ListOfCarsDrivenByDriver")
                    private List<Car_OneToMany> listOfCarsBeingDriven = new ArrayList<Car_OneToMany>();
                

                是什么让 Hibernate 抛出异常:

                此异常会在您的控制台中引发,因为我附加到父对象的子对象此时不在数据库中。

                通过提供 @OneToMany(cascade = {CascadeType.ALL}) ,它告诉 Hibernate 在保存父对象的同时将它们保存到数据库中。

                【讨论】:

                  【解决方案10】:

                  案例 1: 当我尝试创建父级并将该父级引用保存到其子级以及其他一些 DELETE/UPDATE 查询(JPQL)时,我遇到了这个异常。所以我只是在创建父对象和使用相同的父引用创建子对象之后刷新()新创建的实体。它对我有用。

                  案例2:

                  父类

                  public class Reference implements Serializable {
                  
                      @Id
                      @Column(precision=20, scale=0)
                      private BigInteger id;
                  
                      @Temporal(TemporalType.TIMESTAMP)
                      private Date modifiedOn;
                  
                      @OneToOne(mappedBy="reference")
                      private ReferenceAdditionalDetails refAddDetails;
                      . 
                      .
                      .
                  }
                  

                  儿童班:

                  public class ReferenceAdditionalDetails implements Serializable{
                  
                      private static final long serialVersionUID = 1L;
                  
                      @Id
                      @OneToOne
                      @JoinColumn(name="reference",referencedColumnName="id")
                      private Reference reference;
                  
                      private String preferedSector1;
                      private String preferedSector2;
                      .
                      .
                  
                  }
                  

                  在上述情况下,父(Reference)和子(ReferenceAdditionalDetails)具有 OneToOne 关系,当您尝试创建引用实体及其子实体(ReferenceAdditionalDetails)时,它会给您同样的异常。因此,为了避免异常,您必须为子类设置 null 然后创建父类。(示例代码)

                  .
                  .
                  reference.setRefAddDetails(null);
                  reference = referenceDao.create(reference);
                  entityManager.flush();
                  .
                  .
                  

                  【讨论】:

                  • 我遇到了类似于案例 2 的问题,我在父 P1 对象中有一个 C1 对象引用,并且 C1 对象是可选的,当用户不保存 P1 对象时,我必须显式地使 C1 对象为空添加 C1 对象。
                  【解决方案11】:

                  我认为是因为您尝试持久化一个对象,该对象具有对另一个尚未持久化的对象的引用,因此它尝试在“数据库端”中对不存在的行进行引用

                  【讨论】:

                    【解决方案12】:

                    为了完整起见:A

                    org.hibernate.TransientPropertyValueException 
                    

                    有消息

                    object references an unsaved transient instance - save the transient instance before flushing
                    

                    当您尝试将一个实体与对另一个恰好分离的实体的引用进行持久化/合并时也会发生。

                    【讨论】:

                      【解决方案13】:

                      当我没有保留父对象但我正在保存孩子时,我遇到了这个异常。为了解决这个问题,我在同一个会话中保留了子对象和父对象,并在父对象上使用了 CascadeType.ALL。

                      【讨论】:

                        【解决方案14】:

                        如果您使用的是 Spring Data JPA,那么在您的服务实现中添加 @Transactional 注释将解决该问题。

                        【讨论】:

                          【解决方案15】:

                          此错误的可能性很多,添加页面或编辑页面上也有一些其他可能性。就我而言,我试图保存一个对象 AdvanceSalary。问题是在编辑时 AdvanceSalary employee.employee_id 为空 因为在编辑时我没有设置employee.employee_id。我做了一个隐藏的字段并设置它。我的代码运行良好。

                              @Entity(name = "ic_advance_salary")
                              @Table(name = "ic_advance_salary")
                              public class AdvanceSalary extends BaseDO{
                          
                                  @Id
                                  @GeneratedValue(strategy = GenerationType.IDENTITY)
                                  @Column(name = "id")
                                  private Integer id;
                          
                                  @ManyToOne(fetch = FetchType.EAGER)
                                  @JoinColumn(name = "employee_id", nullable = false)
                                  private Employee employee;
                          
                                  @Column(name = "employee_id", insertable=false, updatable=false)
                                  @NotNull(message="Please enter employee Id")
                                  private Long employee_id;
                          
                                  @Column(name = "advance_date")
                                  @DateTimeFormat(pattern = "dd-MMM-yyyy")
                                  @NotNull(message="Please enter advance date")
                                  private Date advance_date;
                          
                                  @Column(name = "amount")
                                  @NotNull(message="Please enter Paid Amount")
                                  private Double amount;
                          
                                  @Column(name = "cheque_date")
                                  @DateTimeFormat(pattern = "dd-MMM-yyyy")
                                  private Date cheque_date;
                          
                                  @Column(name = "cheque_no")
                                  private String cheque_no;
                          
                                  @Column(name = "remarks")
                                  private String remarks;
                          
                                  public AdvanceSalary() {
                                  }
                          
                                  public AdvanceSalary(Integer advance_salary_id) {
                                      this.id = advance_salary_id;
                                  }
                          
                                  public Integer getId() {
                                      return id;
                                  }
                          
                                  public void setId(Integer id) {
                                      this.id = id;
                                  }
                          
                                  public Employee getEmployee() {
                                      return employee;
                                  }
                          
                                  public void setEmployee(Employee employee) {
                                      this.employee = employee;
                                  }
                          
                          
                                  public Long getEmployee_id() {
                                      return employee_id;
                                  }
                          
                                  public void setEmployee_id(Long employee_id) {
                                      this.employee_id = employee_id;
                                  }
                          
                              }
                          

                          【讨论】:

                            【解决方案16】:

                            错误的一个可能原因是不存在父实体的值的设置;例如,对于部门-雇员关系,您必须这样写才能修复错误:

                            Department dept = (Department)session.load(Department.class, dept_code); // dept_code is from the jsp form which you get in the controller with @RequestParam String department
                            employee.setDepartment(dept);
                            

                            【讨论】:

                              【解决方案17】:

                              我相信这可能只是重复的答案,但为了澄清一下,我在 @OneToOne 映射和 @OneToMany 上得到了这个。在这两种情况下,事实上我添加到ParentChild 对象还没有保存在数据库中。因此,当我将Child 添加到Parent,然后保存Parent 时,Hibernate 在保存Parent 时会抛出"object references an unsaved transient instance - save the transient instance before flushing" 消息。

                              Parent's 上添加cascade = {CascadeType.ALL}Child 的引用解决了这两种情况下的问题。这保存了ChildParent

                              抱歉有任何重复的答案,只是想进一步澄清一下。

                              @OneToOne(cascade = {CascadeType.ALL})
                              @JoinColumn(name = "performancelog_id")
                              public PerformanceLog getPerformanceLog() {
                                  return performanceLog;
                              }
                              

                              【讨论】:

                              • 如果我不想级联保存 @OneToOne 关系怎么办?第一次创建两个对象时,如何在不触发异常的情况下将其中一个保存到数据库中?
                              • 如果我想在某些情况下拯救孩子而不是在其他情况下怎么办?
                              • @xtian:那么您必须通过使用 EntityManager 持久化对象来处理保存到数据库的正确顺序。基本上你只是说 em.persist(object1); em.persist(object2);等
                              • 我在使用@Inheritance 时特别​​遇到了这个问题,在本例中是 TABLE_PER_CLASS,我引用了一个子类。 CascadeType.ALL 修复了它。
                              • 或者您已经使用new MyEntity 创建了实体对象(没有将其同步到数据库 - 刷新),而不是从数据库中获取其同步实例。使用该实例进行 Hibernate 查询会通知您,您期望在数据库中的内容与您在应用程序内存中的内容不同。在这种情况下 - 只需从数据库同步/获取您的实体实例并使用它。那么就不需要 CascadeType.ALL。
                              【解决方案18】:

                              除非真的需要,否则不要使用Cascade.AllRolePermission 具有双向 manyToMany 关系。那么下面的代码就可以正常工作了

                                  Permission p = new Permission();
                                  p.setName("help");
                                  Permission p2 = new Permission();
                                  p2.setName("self_info");
                                  p = (Permission)crudRepository.save(p); // returned p has id filled in.
                                  p2 = (Permission)crudRepository.save(p2); // so does p2.
                                  Role role = new Role();
                                  role.setAvailable(true);
                                  role.setDescription("a test role");
                                  role.setRole("admin");
                                  List<Permission> pList = new ArrayList<Permission>();
                                  pList.add(p);
                                  pList.add(p2);
                                  role.setPermissions(pList);
                                  crudRepository.save(role);
                              

                              如果对象只是一个“新”对象,那么它会抛出同样的错误。

                              【讨论】:

                              • 100% 正确,Cascade.All 是惰性解决方案,仅应在需要时应用。首先,如果实体已经存在,则检查是否在当前实体管理器中加载,如果没有加载。
                              【解决方案19】:

                              为了增加我的 2 美分,当我不小心将 null 作为 ID 发送时,我遇到了同样的问题。下面的代码描述了我的场景(并且 OP 没有提到任何具体场景)

                              Employee emp = new Employee();
                              emp.setDept(new Dept(deptId)); // --> when deptId PKID is null, same error will be thrown
                              // calls to other setters...
                              em.persist(emp);
                              

                              在这里,我将现有部门 id 设置为新员工实例,而实际上并未首先获取部门实体,因为我不想触发另一个选择查询。

                              在某些情况下,deptId PKID 来自调用方法的null,我得到了同样的错误。

                              所以,请注意null PK ID 的值

                              【讨论】:

                              • 如果它可以为空怎么办?
                              • 我也有类似的问题。当我的 deptID 为 0 时出现异常。任何其他大于 0 的值都有效。有趣的是我有一个 id = 0 的部门。
                              【解决方案20】:

                              还有另一种可能导致休眠中出现此错误。您可以将对象A 的未保存引用设置为附加实体B,并希望保留对象C。即使在这种情况下,您也会收到上述错误。

                              【讨论】:

                                【解决方案21】:

                                除了所有其他好的答案之外,如果您使用merge 来持久化一个对象并且不小心忘记在父类中使用该对象的合并引用,则可能会发生这种情况。考虑下面的例子

                                merge(A);
                                B.setA(A);
                                persist(B);
                                

                                在这种情况下,您合并了A,但忘记使用A 的合并对象。要解决这个问题,你必须像这样重写代码。

                                A=merge(A);//difference is here
                                B.setA(A);
                                persist(B);
                                

                                【讨论】:

                                  【解决方案22】:

                                  解决此问题的简单方法是保存两个实体。 首先保存子实体,然后保存父实体。 因为父实体依赖于子实体的外键值。

                                  下面是一对一关系的简单考试

                                  insert into Department (name, numOfemp, Depno) values (?, ?, ?)
                                  Hibernate: insert into Employee (SSN, dep_Depno, firstName, lastName, middleName, empno) values (?, ?, ?, ?, ?, ?)
                                  
                                  Session session=sf.openSession();
                                          session.beginTransaction();
                                          session.save(dep);
                                          session.save(emp);
                                  

                                  【讨论】:

                                  • 正好相反——子实体拥有FK值,依赖于父实体,所以你需要先保存父实体!在代码块中你做对了。
                                  【解决方案23】:

                                  另一个可能的原因:在我的例子中,我试图在一个全新的实体上拯救父母之前拯救孩子。

                                  User.java 模型中的代码是这样的:

                                  this.lastName = lastName;
                                  this.isAdmin = isAdmin;
                                  this.accountStatus = "Active";
                                  this.setNewPassword(password);
                                  this.timeJoin = new Date();
                                  create();
                                  

                                  setNewPassword() 方法创建一个 PasswordHistory 记录并将其添加到 User 中的历史记录集合中。由于尚未为父级执行 create() 语句,因此它试图保存到尚未创建的实体的集合中。我所要做的就是在调用 create() 之后移动 setNewPassword() 调用。

                                  this.lastName = lastName;
                                  this.isAdmin = isAdmin;
                                  this.accountStatus = "Active";
                                  this.timeJoin = new Date();
                                  create();
                                  this.setNewPassword(password);
                                  

                                  【讨论】:

                                    【解决方案24】:

                                    我在使用时遇到此错误

                                    getSession().save(object)
                                    

                                    但是当我使用时它没有问题

                                    getSession().saveOrUpdate(object) 
                                    

                                    【讨论】:

                                      【解决方案25】:

                                      这不是错误的唯一原因。我刚才遇到了它,因为我的编码中有一个拼写错误,我相信它设置了一个已经保存的实体的值。

                                      X x2 = new X();
                                      x.setXid(memberid); // Error happened here - x was a previous global entity I created earlier
                                      Y.setX(x2);
                                      

                                      我通过准确查找导致错误的变量(在本例中为 String xid)发现了错误。我在保存实体并打印跟踪的整个代码块周围使用了catch

                                      {
                                         code block that performed the operation
                                      } catch (Exception e) {
                                         e.printStackTrace(); // put a break-point here and inspect the 'e'
                                         return ERROR;
                                      }
                                      

                                      【讨论】:

                                      • 与我的问题类似。毕竟,当我在本地重新加载实体,设置属性,然后保存时,它工作正常。
                                      【解决方案26】:

                                      在我的情况下,这是由于双向关系的 @ManyToOne 一侧没有 CascadeType 引起的。更准确地说,我在@OneToMany 一侧有CascadeType.ALL,而在@ManyToOne 上没有。将CascadeType.ALL 添加到@ManyToOne 解决了这个问题。 一对多端:

                                      @OneToMany(cascade = CascadeType.ALL, mappedBy="globalConfig", orphanRemoval = true)
                                      private Set<GlobalConfigScope>gcScopeSet;
                                      

                                      多对一(导致问题)

                                      @ManyToOne
                                      @JoinColumn(name="global_config_id")
                                      private GlobalConfig globalConfig;
                                      

                                      多对一(通过添加CascadeType.PERSIST修复)

                                      @ManyToOne(cascade = CascadeType.PERSIST)
                                      @JoinColumn(name="global_config_id")
                                      private GlobalConfig globalConfig;
                                      

                                      【讨论】:

                                      • 如果我这样做,并保存父实体,发生的情况是我的父表中有两个插入,即两行。我认为这是因为我们在父实体和子实体上都有级联?
                                      【解决方案27】:

                                      如果您的集合可以为空,请尝试:object.SetYouColection(null);

                                      【讨论】:

                                      • 这完全是我的问题。我永远不会猜到我必须手动将其设置为 null。
                                      • 这也是我的问题。我没有使用集合,所以我一开始没有尝试这个,但是我将我的对象设置为 null,现在它可以工作了。
                                      【解决方案28】:

                                      我在持久化一个实体时发生这种情况,其中数据库中的现有记录对于带有 @Version 注释的字段具有 NULL 值(用于乐观锁定)。将数据库中的 NULL 值更新为 0 更正了此问题。

                                      【讨论】:

                                      • 这应该是一个新问题,应该作为错误添加,至少是误导性异常。这原来是我的问题的原因。
                                      • 这解决了我的问题
                                      【解决方案29】:

                                      当 Hibernate 认为它需要保存与您正在保存的对象关联的对象时保存对象时会发生这种情况。

                                      我遇到了这个问题,不想保存对引用对象的更改,所以我希望级联类型为 NONE。

                                      诀窍是确保设置了引用对象中的ID和VERSION,这样Hibernate就不会认为引用的对象是需要保存的新对象。这对我有用。

                                      查看您正在保存的类中的所有关系以计算出关联对象(以及关联对象的关联对象),并确保在对象树的所有对象中设置了 ID 和 VERSION。

                                      【讨论】:

                                      • 这条评论让我走上了正轨。我正在将父级的新实例分配给其子级的属性。所以 NH 认为它们是不同的实例。
                                      • 是的。例如,如果关联对象的 id 不包括在内(例如,它被 @JsonIgnore 忽略),就会发生这种情况。 Hibernate 无法识别关联的实体,所以它想保存它。
                                      • 谢谢!我有一个外键引用的不是引用表上的 ID 字段,而是一个具有 UNIQUE 约束 (CODE) 的字段,并且在创建时 Hibernate 无法从给定的 CODE 中找到现有行并尝试在引用表上创建它.我通过从其 CODE 中预取整个实体(包括 ID)来解决,然后在保存之前放入 Java 实体。这次 Hib 没有尝试创建行,错误消失了。
                                      【解决方案30】:

                                      或者,如果您想使用最小的“权力”(例如,如果您不想要级联删除)来实现您想要的,请使用

                                      import org.hibernate.annotations.Cascade;
                                      import org.hibernate.annotations.CascadeType;
                                      
                                      ...
                                      
                                      @Cascade({CascadeType.SAVE_UPDATE})
                                      private Set<Child> children;
                                      

                                      【讨论】:

                                      • 这应该是公认的答案。 CascadeType.ALL 过于宽泛
                                      • 从 Hibernate 5.2.8 开始,似乎没有办法使用 JPA 注释实现相同的效果。例如,@ManyToMany(cascade={PERSIST, MERGE, REFRESH, DETACH})(除了 REMOVE 之外的所有)不会像 Hibernate 的 CascadeType.SAVE_UPDATE 那样级联更新。
                                      猜你喜欢
                                      • 2021-03-03
                                      • 2021-06-26
                                      • 1970-01-01
                                      • 1970-01-01
                                      • 2012-05-03
                                      相关资源
                                      最近更新 更多