Use JSON duality view in Oracle
Learn how to use JSON duality views in Micronaut
On this guide
In this section
Getting Started
In this guide, we will create a Micronaut application written in Kotlin.
In this guide, we will set up and use an Oracle JSON Relational Duality View with Micronaut Data. The application includes classes that represent a JSON Relational Duality View in Oracle Database. In the background, the SQL CREATE statement for the view is generated and executed.
What Is a JSON Relational Duality View
A JSON Relational Duality View in Oracle Database lets you map relational tables to JSON documents. This creates a document-style interface for data stored relationally.
JSON Relational Duality is a new data modeling capability that features updatable and consistent JSON document views over relational data. This allows data that is stored efficiently in relational tables to be accessed as simple JSON documents. JSON Relational Duality Views can be accessed with document APIs, such as MongoDB-compatible APIs, REST, and SQL.
Usage
In Micronaut, JSON duality views are supported by annotating an entity with @JsonView.
What you will need
To complete this guide, you will need the following:
-
Some time on your hands
-
A decent text editor or IDE
-
JDK 1.8 or greater installed with
JAVA_HOMEconfigured appropriately -
Docker installed to run MySQL and to run tests using Testcontainers.
Writing the application
In Micronaut, users don’t have to use SQL to create JSON duality views and execute CRUD operations on them. By annotating classes with @JsonView, users specify which views to generate. CRUD operations are executed via PageableRepository.
High-level diagram of the classes used

Writing an entity class
For each table in the database, we can create a corresponding entity class. For the Student table, we can create the following class:
Writing a view class
A class annotated with @JsonView generates the corresponding JSON duality view. The @JsonView annotation has an entity field, which represents the class for which the view will be created; in our case, Student.class. It is a required field.
Create the view class:
Using a flex column
The extras map on Student and StudentView is annotated with @JsonAnyGetter and @JsonAnySetter. Micronaut Data maps it to an Oracle JSON flex column: declared properties remain relational fields, while arbitrary properties such as department or remote can be added to the JSON document without adding Java, Groovy, or Kotlin fields or table columns.
Writing subview classes
As you can see from the StudentView.classes relation, JSON duality views support table joins. They are specified by using the @Relation annotation.
For every relation, a subview is created. A subview class must be created for each relation. To specify a subview class, we use the @JsonSubView annotation. @JsonSubView also has an entity field with the same meaning; it is required as well. These classes represent subviews; CREATE statements are generated only for classes annotated with @JsonView.
Define the following entity and corresponding subview classes:
Entity classes
A student can enroll in multiple classes, and each class can include multiple students, so we model this many-to-many relationship using a join table represented by StudentClass that links Student and Class. That’s why we have two MANY_TO_ONE relations in StudentClass.
Subview classes
package example.micronaut.domain
import com.fasterxml.jackson.annotation.JsonProperty
import io.micronaut.data.annotation.Id
import io.micronaut.data.annotation.Relation
import io.micronaut.data.annotation.JsonSubView
import io.micronaut.serde.annotation.Serdeable
@Serdeable
@JsonSubView(entity = StudentClass::class)
data class StudentScheduleSubView(
@Id
val id: Long,
@JsonProperty("class")
@Relation(Relation.Kind.ONE_TO_ONE)
val clazz: StudentScheduleClassSubView
)The resulting SQL CREATE statement
The provided classes will generate the following view at build time:
CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW student_view AS
SELECT JSON {
'_id': s.id,
'name': s.name,
s.extras AS FLEX COLUMN,
'classes': [
SELECT JSON {
'id': sc.id,
'class': (
SELECT JSON {
'classID': c.id,
'name': c.name
}
FROM TBL_CLASS c
WITH INSERT UPDATE
WHERE sc."CLASS_ID"=c."ID"
)
}
FROM TBL_STUDENT_CLASSES sc
WITH UPDATE INSERT DELETE
WHERE s."ID"=sc."STUDENT_ID"
]
}
FROM TBL_STUDENT s WITH UPDATE INSERT DELETE;Writing Tests
This is a sample repository that uses the StudentView class, including arbitrary flex-column properties:
package example.micronaut
import io.micronaut.data.jdbc.annotation.JdbcRepository
import io.micronaut.data.model.query.builder.sql.Dialect
import io.micronaut.data.repository.PageableRepository
import example.micronaut.domain.StudentView
import java.util.Optional
@JdbcRepository(dialect = Dialect.ORACLE)
interface StudentViewRepository : PageableRepository<StudentView, Long> {
fun findByName(name: String): Optional<StudentView>
}Create a test to verify CRUD operations using the StudentViewRepository:
package example.micronaut
import example.micronaut.domain.StudentScheduleClassSubView
import example.micronaut.domain.StudentScheduleSubView
import example.micronaut.domain.StudentView
import io.micronaut.test.extensions.junit5.annotation.MicronautTest
import jakarta.inject.Inject
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
@MicronautTest
class StudentViewRepositoryTest {
@Inject
lateinit var studentViewRepository: StudentViewRepository
@Inject
lateinit var classRepository: ClassRepository
@Test
fun testCreateStudentView() {
val mathClass = classRepository.save(example.micronaut.domain.Class(null, "Math"))
val studentScheduleClassSubView = StudentScheduleClassSubView(mathClass.id, mathClass.name)
val studentScheduleSubView = StudentScheduleSubView(0L, studentScheduleClassSubView)
val studentView = StudentView(null, "John", listOf(studentScheduleSubView), mapOf("department" to "Research", "remote" to true))
studentViewRepository.save(studentView)
val student = studentViewRepository.findByName(studentView.name)
assertTrue(student.isPresent)
val savedStudent = student.orElseThrow()
assertEquals("Research", savedStudent.extras["department"])
assertEquals(true, savedStudent.extras["remote"])
assertEquals(1, savedStudent.classes.size)
assertEquals("Math", savedStudent.classes[0].clazz.name)
}
}Testing the Application
To run the tests:
./gradlew testThen open build/reports/tests/test/index.html in a browser to see the results.
Running the Application
To run the application, use the ./gradlew run command, which starts the application on port 8080.
Next Steps
Read more about Micronaut Data and JSON Duality Views.
Help with the Micronaut Framework
The Micronaut Foundation sponsored the creation of this Guide. A variety of consulting and support services are available.
License
|
Note
|
All guides are released with an Apache License 2.0 for the code and a Creative Commons Attribution 4.0 license for the writing and media (images). |