jpa 2.0 - ejemplo - No se puede hacer que la relación @ManyToOne sea anulable
jpa relaciones (4)
Tengo una relación de varios a uno que quiero que sea anulable:
@ManyToOne(optional = true)
@JoinColumn(name = "customer_id", nullable = true)
private Customer customer;
Desafortunadamente, JPA sigue configurando la columna en mi base de datos como NO NULA. ¿Alguien puede explicar esto? ¿Hay alguna manera de hacerlo funcionar? Tenga en cuenta que uso JBoss 7, JPA 2.0 con Hibernate como proveedor de persistencia y una base de datos PostgreSQL 9.1.
EDITAR :
He encontrado la causa de mi problema. Aparentemente se debe a la forma en que definí la clave principal en la entidad referenciada Customer
:
@Entity
@Table
public class Customer {
@Id
@GeneratedValue
@Column(columnDefinition="serial")
private int id;
}
Parece que el uso de @Column(columnDefinition="serial")
para la clave principal establece automáticamente las claves externas que hacen referencia a NOT NULL
en la base de datos. ¿Es ese realmente el comportamiento esperado al especificar el tipo de columna como serial
? ¿Hay una solución para habilitar claves foráneas que puedan ser anuladas en este caso?
Gracias de antemano.
Encontré este problema pero pude resolverlo de esta manera:
@ManyToOne
@JoinColumn(nullable = true)
private Customer customer;
Quizás el problema surgió al declarar @ManyToOne(optional = true)
Encontré la solución a mi problema. La forma en que se define la clave principal en la entidad Customer
está bien, el problema reside en la declaración de clave externa. Se debe declarar así:
@ManyToOne
@JoinColumn(columnDefinition="integer", name="customer_id")
private Customer customer;
De hecho, si se omite el atributo columnDefinition="integer"
la clave foránea se establecerá de manera predeterminada como la columna de origen: una serie no nula con su propia secuencia. Por supuesto, eso no es lo que queremos, ya que solo queremos que haga referencia al ID de incremento automático, no para crear uno nuevo.
Además, parece que el atributo name=customer_id
también se requiere como observé al realizar algunas pruebas. De lo contrario, la columna de clave externa se establecerá como la columna de origen. Este es un comportamiento extraño en mi opinión. Comentarios o información adicional para aclarar esto son bienvenidos!
Finalmente, la ventaja de esta solución es que la ID es generada por la base de datos (no por JPA) y, por lo tanto, no tenemos que preocuparnos al insertar datos manualmente o mediante scripts, lo que suele suceder en la migración o el mantenimiento de los datos.
Eso es muy raro.
En JPA, el parámetro anulable es verdadero de forma predeterminada. Uso este tipo de configuración todo el tiempo y funciona bien. Si intentas salvar la entidad debería tener éxito.
¿Intentó eliminar la tabla que se crea para esta relación? ¿Tal vez tienes tabla legada con esa columna?
O tal vez debería intentar encontrar una solución en otros trozos de código, porque esta es la configuración adecuada.
Nota: He intentado esta configuración en PostgreSQL con JPA2 e Hibernate.
EDITAR
En ese caso, puede intentar una definición un poco diferente de la clave principal.
Por ejemplo, puedes usar una definición como esta:
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column()
private Long id;
y postgresql generará
id bigint NOT NULL
-- with constraint
CONSTRAINT some_table_pkey PRIMARY KEY (id)
Si esto es lo suficientemente bueno, puedes probar esta solución.
dentro de la transacción pero antes de la operación de guardar, establezca explícitamente el valor de la columna de clave externa como nulo. Mediante esta hibernación, nunca realice consultas de selección para esta tabla relacionada con la clave externa y no lance la excepción "guardar la instancia transitoria antes de vaciar". si desea establecer el "valor nulo" condicionalmente, entonces realice 1. busque y establezca el valor usando la llamada repo get / find 2. luego verifique el valor recuperado de la condición y establézcalo en nulo en consecuencia. pegó el código siguiente que se prueba y encontrado trabajando
// Transaction Start Optional<Customer> customerObject = customerRepository.findByCustomerId(customer.getCustomerId()) if(customerObject.isPresent())yourEnclosingEntityObject.setCustomer(customerObject)} else {yourEnclosingEntityObject.setCustomer(null)} yourEnclosingEntityObjectRepository.save(yourEnclosingEntityObject) // Transaction End