【问题标题】:Spring REST API method for update object doesn't retrieve existing sub-objects更新对象的 Spring REST API 方法不检索现有的子对象
【发布时间】:2019-03-14 04:33:50
【问题描述】:

我有一个基于 Spring Boot + Hibernate + mySQL 的 REST API,它具有用于 CRUD 操作和数据检索的常用方法。模型中有几个多对多关系,我将展示我的问题的相关类:

Pectest.java

@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="idtest")
public class Pectest {
    @Id
    @GeneratedValue
    @Column(name="idtest")
    private int idtest=-1;
    private String testname;
    private String testdescription;
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="idtesttype")
    private Testtype testtype;
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="iddatatype")
    private Datatype datatype;
    private String testurl;
    private Date datecreated;
    private Date lastupdated;

    @ManyToMany(cascade = { 
            CascadeType.PERSIST, 
            CascadeType.MERGE
        })
    @JoinTable(name = "test_checks_requisite",
            joinColumns = @JoinColumn(name = "test_idtest"),
            inverseJoinColumns = @JoinColumn(name = "requisite_idrequisite")
        )
    private Set<Requisite> requisites = new HashSet<Requisite>();

// Getters and setters
[...]
}

Requisite.java

@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="idrequisite")
public class Requisite {
    @Id
    @GeneratedValue //(strategy=GenerationType.IDENTITY)
    @Column(name="idrequisite")
    private int idrequisite;
    private String Name;
    private String Title;
    private String Description;
    @ManyToOne(cascade = {CascadeType.PERSIST}, fetch=FetchType.LAZY)
    @JoinColumn(name="functionality_idfunctionality")
    private Functionality functionality; 

    @ManyToMany(mappedBy="requisites")
    private Set<Pectest> tests = new HashSet<Pectest>();

// Getters and setters
[...]
}

两者的 GET 方法都按预期工作,正确检索给定 PectestRequisite 列表。但是,当我尝试通过调用相应的 API 入口点来更新现有的 Pectest 时,我发现它不会从数据库中获取 JSON 中现有的必要条件,给它们一个 idrequisite 值 0(如如下所示)。因此,Hibernate“决定”它们应该作为新的插入。由于数据库中的唯一约束而失败,但问题是根本不应该插入它们。

Pectest 对象的更新过程可能涉及(除了明显的名称、描述等、修改之外)向其列表添加更多 Requisite 元素,但这些元素将永远不要在那时自己更新。

这里有所有额外的信息:

传递给 API 的 PUT 方法的 JSON 数据示例

    {
  "idtest": 9,
  "testtype": { "idTestType": 5, "testTypeName": "Manual" },
  "datatype": null,
  "requisites": [
    {
      "idrequisite": 2,
      "functionality": {
        "idfunctionality": 1002,
        "functionalityName": "Data Access OPC-UA",
        "parentFunctionality": {
          "idfunctionality": 1000,
          "functionalityName": "OPC-UA",
          "parentFunctionality": null,
          "childrenFunctionalities": [
            {
              "idfunctionality": 1006,
              "functionalityName": "Alarmas OPC-UA",
              "parentFunctionality": 1000,
              "childrenFunctionalities": [],
              "description": "Para el tratamiento de las Alarmas del producto BR, se utilizará el tipo propietario AlarmEventType, heredado del tipo básico de alarmas OffNormalAlarmType definido por la especificación OPC UA (Part 9). El Servidor OPC-UA deberá generar un nodo AlarmEventType para cada una de las alarmas definidas en el proyecto en concreto del producto BR (hasta 8192 alarmas distintas)."
            },
            {
              "idfunctionality": 1001,
              "functionalityName": "Atributos OPC-UA",
              "parentFunctionality": 1000,
              "childrenFunctionalities": [],
              "description": "Atributos"
            },
            1002,
            {
              "idfunctionality": 1009,
              "functionalityName": "Device Integration OPC-UA",
              "parentFunctionality": 1000,
              "childrenFunctionalities": [
                {
                  "idfunctionality": 1011,
                  "functionalityName": "Tipo FieldDeviceType OPC-UA",
                  "parentFunctionality": 1009,
                  "childrenFunctionalities": [],
                  "description": "A partir del tipo estándar DeviceType definido por “OPC UA Device Integration” se crea el tipo propietario FieldDeviceType para representar e identificar al producto BR."
                }
              ],
              "description": "A partir del tipo estándar DeviceType definido por “OPC UA Device Integration” se crea el tipo propietario FieldDeviceType para representar e identificar al producto BR."
            },
            {
              "idfunctionality": 1012,
              "functionalityName": "Global Discovery Server OPC-UA",
              "parentFunctionality": 1000,
              "childrenFunctionalities": [],
              "description": "El servidor OPC UA implementará el “Global Certificate Management Server Facet” para comunicarse con el GDS "
            },
            {
              "idfunctionality": 1008,
              "functionalityName": "Historical Access OPC-UA",
              "parentFunctionality": 1000,
              "childrenFunctionalities": [],
              "description": "Esta funcionalidad es resuelta íntegramente por el Servidor OPC-UA, ya que el FW embebido no ofrece soporte para ella."
            },
            {
              "idfunctionality": 1007,
              "functionalityName": "Monitoring & Subscription OPC-UA",
              "parentFunctionality": 1000,
              "childrenFunctionalities": [],
              "description": "Subscripciones para visualización de datos desde un cliente OPC-UA"
            },
            {
              "idfunctionality": 1010,
              "functionalityName": "Seguridad OPC-UA",
              "parentFunctionality": 1000,
              "childrenFunctionalities": [],
              "description": "El servidor OPC-UA debe implementar una serie de medidas de seguridad para garantizar la confidencialidad de la información."
            }
          ],
          "description": "OPC-UA Server"
        },
        "childrenFunctionalities": [
          {
            "idfunctionality": 1004,
            "functionalityName": "AnalogType OPC-UA",
            "parentFunctionality": 1002,
            "childrenFunctionalities": [],
            "description": "Este tipo propietario se utiliza para los nodos OPC-UA de variables no booleanas del producto BR que no sean parámetros."
          },
          {
            "idfunctionality": 1003,
            "functionalityName": "DigitalType OPC-UA",
            "parentFunctionality": 1002,
            "childrenFunctionalities": [],
            "description": "Tipo propietario de datos booleanos servido por OPC-UA, que no sean parámetros."
          },
          {
            "idfunctionality": 1005,
            "functionalityName": "ParameterType OPC-UA",
            "parentFunctionality": 1002,
            "childrenFunctionalities": [],
            "description": "Este tipo propietario se utiliza para los nodos OPC-UA de variables del producto BR que estén identificadas como parámetros. El servidor OPC-UA debe identificar dichas variables del producto BR como aquellas que tengan atributo ParamGID. "
          }
        ],
        "description": "Se ofrecerá acceso a las variables exportadas por el FW de la CPU del producto BR a través de tres tipos propietarios de nodos: DigitalType, AnalogType y ParameterType"
      },
      "name": "REQ-0001",
      "description": "Se ofrece acceso a variables digitales a través de este tipo de nodo",
      "title": "Digital Type"
    },
    {
      "idrequisite": 4,
      "functionality": 1002,
      "name": "REQ-0003",
      "description": "Se ofrece acceso a parámetros a través de este tipo de nodo",
      "title": "Parameter Type"
    },
    {
      "idrequisite": 3,
      "functionality": 1002,
      "name": "REQ-0002",
      "description": "Se ofrece acceso a variables analógicas a través de este tipo de nodo",
      "title": "Analog Type"
    }
  ],
  "testDescription": "El servidor es capaz de devolver correctamente todos los tipos de dato.",
  "testURL": "",
  "testName": "Lectura de DataTypes",
  "dateCreated": "09-05-2018 08:57:27",
  "lastUpdated": "09-05-2018 08:57:27"
}

PecTestController.java

[...]
@PostMapping(path="/test", produces= MediaType.APPLICATION_JSON_VALUE)
    public Pectest createOrUpdateTest(@Valid @RequestBody Pectest newtest) {
        try {
            System.out.println("Create or update Test: " + objectmapper.writeValueAsString(newtest));
        } catch (JsonProcessingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return testService.saveOrUpdate(newtest);
    }

控制台输出(格式化)

  {
      "idtest": 9,
      "testtype": { "idTestType": 5, "testTypeName": "Manual" },
      "datatype": null,
      "requisites": [
        {
          "idrequisite": 0,
          "functionality": {
            "idfunctionality": 0,
            "functionalityName": "Data Access OPC-UA",
            "parentFunctionality": {
              "idfunctionality": 0,
              "functionalityName": "OPC-UA",
              "parentFunctionality": null,
              "childrenFunctionalities": [
                {
                  "idfunctionality": 0,
                  "functionalityName": "Device Integration OPC-UA",
                  "parentFunctionality": 0,
                  "childrenFunctionalities": [
                    {
                      "idfunctionality": 0,
                      "functionalityName": "Tipo FieldDeviceType OPC-UA",
                      "parentFunctionality": 0,
                      "childrenFunctionalities": [],
                      "description": "A partir del tipo estándar DeviceType definido por “OPC UA Device Integration” se crea el tipo propietario FieldDeviceType para representar e identificar al producto BR."
                    }
                  ],
                  "description": "A partir del tipo estándar DeviceType definido por “OPC UA Device Integration” se crea el tipo propietario FieldDeviceType para representar e identificar al producto BR."
                },
                {
                  "idfunctionality": 0,
                  "functionalityName": "Historical Access OPC-UA",
                  "parentFunctionality": 0,
                  "childrenFunctionalities": [],
                  "description": "Esta funcionalidad es resuelta íntegramente por el Servidor OPC-UA, ya que el FW embebido no ofrece soporte para ella."
                },
                {
                  "idfunctionality": 0,
                  "functionalityName": "Atributos OPC-UA",
                  "parentFunctionality": 0,
                  "childrenFunctionalities": [],
                  "description": "Atributos"
                },
                {
                  "idfunctionality": 0,
                  "functionalityName": "Alarmas OPC-UA",
                  "parentFunctionality": 0,
                  "childrenFunctionalities": [],
                  "description": "Para el tratamiento de las Alarmas del producto BR, se utilizará el tipo propietario AlarmEventType, heredado del tipo básico de alarmas OffNormalAlarmType definido por la especificación OPC UA (Part 9). El Servidor OPC-UA deberá generar un nodo AlarmEventType para cada una de las alarmas definidas en el proyecto en concreto del producto BR (hasta 8192 alarmas distintas)."
                },
                0,
                {
                  "idfunctionality": 0,
                  "functionalityName": "Monitoring & Subscription OPC-UA",
                  "parentFunctionality": 0,
                  "childrenFunctionalities": [],
                  "description": "Subscripciones para visualización de datos desde un cliente OPC-UA"
                },
                {
                  "idfunctionality": 0,
                  "functionalityName": "Seguridad OPC-UA",
                  "parentFunctionality": 0,
                  "childrenFunctionalities": [],
                  "description": "El servidor OPC-UA debe implementar una serie de medidas de seguridad para garantizar la confidencialidad de la información."
                },
                {
                  "idfunctionality": 0,
                  "functionalityName": "Global Discovery Server OPC-UA",
                  "parentFunctionality": 0,
                  "childrenFunctionalities": [],
                  "description": "El servidor OPC UA implementará el “Global Certificate Management Server Facet” para comunicarse con el GDS "
                }
              ],
              "description": "OPC-UA Server"
            },
            "childrenFunctionalities": [
              {
                "idfunctionality": 0,
                "functionalityName": "ParameterType OPC-UA",
                "parentFunctionality": 0,
                "childrenFunctionalities": [],
                "description": "Este tipo propietario se utiliza para los nodos OPC-UA de variables del producto BR que estén identificadas como parámetros. El servidor OPC-UA debe identificar dichas variables del producto BR como aquellas que tengan atributo ParamGID. "
              },
              {
                "idfunctionality": 0,
                "functionalityName": "DigitalType OPC-UA",
                "parentFunctionality": 0,
                "childrenFunctionalities": [],
                "description": "Tipo propietario de datos booleanos servido por OPC-UA, que no sean parámetros."
              },
              {
                "idfunctionality": 0,
                "functionalityName": "AnalogType OPC-UA",
                "parentFunctionality": 0,
                "childrenFunctionalities": [],
                "description": "Este tipo propietario se utiliza para los nodos OPC-UA de variables no booleanas del producto BR que no sean parámetros."
              }
            ],
            "description": "Se ofrecerá acceso a las variables exportadas por el FW de la CPU del producto BR a través de tres tipos propietarios de nodos: DigitalType, AnalogType y ParameterType"
          },
          "name": "REQ-0003",
          "description": "Se ofrece acceso a parámetros a través de este tipo de nodo",
          "title": "Parameter Type"
        },
        {
          "idrequisite": 0,
          "functionality": 0,
          "name": "REQ-0002",
          "description": "Se ofrece acceso a variables analógicas a través de este tipo de nodo",
          "title": "Analog Type"
        },
        {
          "idrequisite": 0,
          "functionality": 0,
          "name": "REQ-0001",
          "description": "Se ofrece acceso a variables digitales a través de este tipo de nodo",
          "title": "Digital Type"
        }
      ],
      "testDescription": "El servidor es capaz de devolver correctamente todos los tipos de dato.",
      "testURL": "",
      "testName": "Lectura de DataTypes",
      "dateCreated": null,
      "lastUpdated": null
    }

如您所见,所有嵌套对象的 id 都为零。为什么 Hibernate 不尝试检索这些元素?模型中是否缺少一些注释?

作为一种解决方法,我正在考虑在更新 Pectest 时忽略整个 Requisite 对象列表,并在 API 中提供一个单独的入口点来添加必要条件将单独调用的测试。

我检查了this 线程,它指向this 示例,但找不到解决方案。

【问题讨论】:

    标签: java mysql json spring hibernate


    【解决方案1】:

    如果您不想要自动生成的 ID,您应该删除注释 @GeneratedValue。 在您的情况下,您正在发送完整的对象(包括 id),因此您不希望休眠生成新的对象

    【讨论】:

    • 这是有道理的,因为 ID 将由 mySQL 在每个 INSERT 上生成,但我删除了两个类上的注释,并且在 @PostMapping 关联方法中,我不断为 Requisite ID。
    • 我已经在没有 @GeneratedValue 的情况下启动了您的示例,并且一切正常:{"idtest":9,"requisites":[{"idrequisite":4, .. 请清除数据库并重试.
    • 我重新启动了所有 3 个部分(数据库、后端和前端),在适用的情况下进行了清理和重新编译,但它仍在为 Requisite ID 分配 0。也许是 Hibernate 或 Spring 配置(一些 Application.properties 设置或类似设置)?我现在有点迷路了,如果它适用于你的场景......
    • 我的输出是 POST 方法中 println 的一个片段(我有很多其他的痕迹,与这个问题无关)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多