偶然发现一个问题:在一对多关系中,似乎从父实体自动生成的主键不会馈送到子实体。
我将生产代码归结为以下示例:
import io.micronaut.data.annotation.GeneratedValue
import io.micronaut.data.annotation.Id
import io.micronaut.data.annotation.MappedEntity
import io.micronaut.data.annotation.Relation
import io.micronaut.data.annotation.Relation.Cascade.PERSIST
import io.micronaut.data.annotation.Relation.Kind.ONE_TO_MANY
import io.micronaut.data.model.naming.NamingStrategies
@MappedEntity("Parent", namingStrategy = NamingStrategies.Raw::class)
data class Parent(
@GeneratedValue @field:Id val id: Long?,
val name: String,
@field:Relation(ONE_TO_MANY, mappedBy = "parent_id", cascade = [PERSIST])
val children: List<Child>? = emptyList()
) {
// Copy-constructor, used by ORM:
constructor(
name: String,
children: List<Child>?
) : this(
null,
name,
children
)
}
@MappedEntity("Child", namingStrategy = NamingStrategies.Raw::class)
data class Child(
@GeneratedValue @field:Id val id: Long?,
val parent_id: Long?,
val description: String,
) {
// Copy-constructor, used by ORM:
constructor(
description: String,
) : this(null, null, description)
}
Run Code Online (Sandbox Code Playgroud)
import io.micronaut.data.annotation.Repository
import io.micronaut.data.jdbc.annotation.JdbcRepository
import io.micronaut.data.model.query.builder.sql.Dialect.H2
import io.micronaut.data.repository.GenericRepository
import javax.transaction.Transactional
@Repository
@JdbcRepository(dialect = H2)
interface ParentRepository : GenericRepository<Parent, Long> {
@Transactional
fun save(parent: Parent): Parent
}
Run Code Online (Sandbox Code Playgroud)
import groovy.sql.Sql
import io.micronaut.test.extensions.spock.annotation.MicronautTest
import jakarta.inject.Inject
import spock.lang.Specification
import javax.sql.DataSource
@MicronautTest
class ParentRepositorySpec extends Specification {
@Inject
DataSource dataSource
@Inject
ParentRepository parentRepository
def 'Inserting parent with single child'() {
given:
loadDatabaseSchemaTo(dataSource)
def child = new Child('Single child')
def parent = new Parent('Parent', List.of(child))
when:
def savedParent = parentRepository.save(parent)
then:
savedParent.id != null // Primary Key field gets populated
savedParent.items[0].id != null // Same with primary key field of child item
}
static void loadDatabaseSchemaTo(DataSource dataSource) {
def createTableParent = '''
CREATE TABLE `Parent` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
)'''
def createTableChild = '''
CREATE TABLE `Child` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`parent_id` int(5) NOT NULL, -- <= Mandatory foreign key
`description` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`parent_id`) REFERENCES `Parent` (`id`)
)'''
new Sql(dataSource).with { sql ->
sql.execute(createTableParent)
sql.execute(createTableChild)
}
}
}
Run Code Online (Sandbox Code Playgroud)
子表外键字段“parent_id”定义为强制:没有父实体的子实体。
但执行测试用例会抛出:
io.micronaut.data.exceptions.DataAccessException: SQL Error executing INSERT: SQL error executing INSERT: NULL not allowed for column "PARENT_ID"; SQL statement:
INSERT INTO `Child` (`parent_id`,`description`) VALUES (?,?) [23502-200]
Run Code Online (Sandbox Code Playgroud)
日志记录显示,先执行对表“Parent”的插入,然后执行对表“Child”的“批量 SQL 插入”...这会中断。
所以对我来说,在发出针对表“child”的 INSERT 语句之前,从父实体自动生成的主键似乎不会传播到子实体的字段“parent_id”。
我的代码中缺少什么?
| 归档时间: |
|
| 查看次数: |
841 次 |
| 最近记录: |