Micronaut Data JDBC: How to map a one-to-many relation using enums? - kotlin

I would like to map a unidirectional one-to-many relation, using Micronaut Data annotations. Considering the object and database models described below, how to map that using Micronaut annotations?
Object model:
import io.micronaut.serde.annotation.Serdeable
#Serdeable
enum class Role {
Admin, Manager
}
import io.micronaut.data.annotation.GeneratedValue
import io.micronaut.data.annotation.Id
import io.micronaut.data.annotation.MappedEntity
import io.micronaut.serde.annotation.Serdeable
#Serdeable
#MappedEntity
data class User(
#field:Id
#field:GeneratedValue
var id: Long? = null,
val username: String,
// How to map?
var roles: Set<Role>?
)
Database model with many-to-many (DDLs for PostgreSQL):
CREATE TABLE "user"
(
id SERIAL PRIMARY KEY,
username VARCHAR(30) NOT NULL UNIQUE
);
CREATE TABLE "role"
(
id INTEGER PRIMARY KEY,
name VARCHAR(40) NOT NULL
);
CREATE TABLE "user_role"
(
user_id INTEGER REFERENCES "user" (id),
role_id INTEGER REFERENCES "role" (id)
);
INSERT INTO "role" (id, name) VALUES (1, 'Admin'), (2, 'Manager');
Or another option, a simpler database model using one-to-many (PostgeSQL):
CREATE TABLE "user"
(
id SERIAL PRIMARY KEY,
username VARCHAR(30) NOT NULL UNIQUE
);
CREATE TYPE role_name AS ENUM ('Admin', 'Manager');
CREATE TABLE "user_role"
(
user_id INTEGER NOT NULL REFERENCES "user" (id),
role_id INTEGER NOT NULL REFERENCES "role" (id),
CONSTRAINT user_role_uk UNIQUE (user_id, role_id)
);
You may suggest modifications to any database model to allow the one-to-many relation in the object model.

Here are two out of the multiple ways that it could be done. I combined them into one example out of laziness.
#Serdeable
enum class Role {
Admin, Manager;
}
#Serdeable
#MappedEntity
data class UserRole(
#field:Id
#GeneratedValue
var id: Long? = null,
#Nullable
#Relation(value = Relation.Kind.MANY_TO_ONE)
val user: User?,
val role: Role
)
#Serdeable
#MappedEntity
class User(
#field:Id
#field:GeneratedValue
var id: Long? = null,
val username: String,
#MappedProperty(definition = "VARCHAR(30)[]", type = DataType.STRING_ARRAY)
var roles: Set<Role>?,
#Relation(value = Relation.Kind.ONE_TO_MANY, mappedBy = "user", cascade = [Cascade.ALL])
var userRoles: Set<UserRole>?
)
#JdbcRepository(dialect = Dialect.POSTGRES)
#Join(value = "userRoles", type = Join.Type.LEFT_FETCH)
interface UserRepository : CrudRepository<User, Long> {
}
CREATE TYPE role_name AS ENUM ('Admin', 'Manager');
CREATE TABLE "user"
(
id SERIAL PRIMARY KEY,
username VARCHAR(30) NOT NULL UNIQUE,
roles VARCHAR(15)[]
);
CREATE TABLE "user_role"
(
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
user_id INTEGER REFERENCES "user" (id),
role role_name NOT NULL,
CONSTRAINT user_role_uk UNIQUE (user_id, role)
);

Related

SQL subquery filters data but conversion to JPA with subquery does not filter

Tree, Treetag and Tag tables with example data
A tree belongs to a given farm and each farm has its own set of tags.
Each tree can have zero or multiple tags attached to it.
Same tag can be attached to multiple trees.
DDL -
DROP TABLE tree_tag;
DROP TABLE tag;
DROP TABLE tree;
DROP TABLE farm;
CREATE TABLE farm (
id VARCHAR2(36 CHAR) NOT NULL,
description VARCHAR2(255 CHAR) NULL,
name VARCHAR2(255 CHAR) NOT NULL,
PRIMARY KEY ( id )
);
CREATE TABLE tree (
id VARCHAR2(36 CHAR) NOT NULL,
name VARCHAR2(255 CHAR) NOT NULL,
description VARCHAR2(255 CHAR) NULL,
farm_id VARCHAR2(36 CHAR) NOT NULL,
PRIMARY KEY ( id )
);
CREATE TABLE tag (
id VARCHAR2(36 CHAR) NOT NULL,
farm_id VARCHAR2(36 CHAR) NOT NULL,
name VARCHAR2(255 CHAR) NOT NULL,
description VARCHAR2(255 CHAR) NULL,
PRIMARY KEY ( id )
);
CREATE TABLE tree_tag (
id VARCHAR2(36 CHAR) NOT NULL,
tag_id VARCHAR2(36 CHAR) NOT NULL,
tree_id VARCHAR2(36 CHAR) NOT NULL,
value VARCHAR2(255 CHAR) NULL,
properties VARCHAR2(1000 CHAR) NULL,
PRIMARY KEY ( id )
);
ALTER TABLE tag ADD CONSTRAINT unq_tag0 UNIQUE ( farm_id,
name );
ALTER TABLE tree_tag ADD CONSTRAINT unq_tree_tag0 UNIQUE ( tag_id,
tree_id );
ALTER TABLE tree ADD CONSTRAINT unq_tree0 UNIQUE ( farm_id,
name );
ALTER TABLE tag
ADD CONSTRAINT fk_tagfarm_id FOREIGN KEY ( farm_id )
REFERENCES farm ( id )
ON DELETE CASCADE;
ALTER TABLE tree_tag
ADD CONSTRAINT fk_tree_tagtree_id FOREIGN KEY ( tree_id )
REFERENCES tree ( id )
ON DELETE CASCADE;
ALTER TABLE tree_tag
ADD CONSTRAINT fk_tree_tagtag_id FOREIGN KEY ( tag_id )
REFERENCES tag ( id )
ON DELETE CASCADE;
INSERT INTO farm (
id,
name
) VALUES (
'farm1',
'farm 1 is big'
);
INSERT INTO farm (
id,
name
) VALUES (
'farm2',
'farm 2 is small'
);
INSERT INTO tag (
id,
farm_id,
name
) VALUES (
'tag11',
'farm1',
'juicy'
);
INSERT INTO tag (
id,
farm_id,
name
) VALUES (
'tag12',
'farm1',
'sour'
);
INSERT INTO tag (
id,
farm_id,
name
) VALUES (
'tag13',
'farm1',
'sweet'
);
INSERT INTO tag (
id,
farm_id,
name
) VALUES (
'tag21',
'farm2',
'sour'
);
INSERT INTO tree (
id,
farm_id,
name,
description
) VALUES (
'tree11',
'farm1',
'apple',
'used for jam'
);
INSERT INTO tree (
id,
farm_id,
name
) VALUES (
'tree12',
'farm1',
'cherry'
);
INSERT INTO tree (
id,
farm_id,
name
) VALUES (
'tree13',
'farm1',
'plum'
);
INSERT INTO tree (
id,
farm_id,
name
) VALUES (
'tree21',
'farm2',
'apple'
);
INSERT INTO tree_tag (
id,
tree_id,
tag_id
) VALUES (
'1',
'tree11',
'tag12'
);
INSERT INTO tree_tag (
id,
tree_id,
tag_id
) VALUES (
'2',
'tree12',
'tag11'
);
INSERT INTO tree_tag (
id,
tree_id,
tag_id
) VALUES (
'3',
'tree12',
'tag13'
);
INSERT INTO tree_tag (
id,
tree_id,
tag_id
) VALUES (
'4',
'tree13',
'tag12'
);
INSERT INTO tree_tag (
id,
tree_id,
tag_id
) VALUES (
'5',
'tree13',
'tag11'
);
Query:
Want to get a list of trees w/o duplicates where either the name
or the description or the tag name matches the filter in a given
farm
Want to get the count of this unique list of trees in a
given farm
This is a paging request so step 1 query will be called multiple times but step 2 query will be executed only once.
SQL -
This works but when I convert to JPA then subquery is returning all the entries in tree_tag i.e filtering is not happening in JPA subquery
SELECT
t.farm_id,
t.id,
t.name,
t.description
FROM
tree t
WHERE
t.farm_id = :farmid
AND ( lower(t.name) LIKE '%'
|| lower(:filter)
|| '%'
OR lower(t.description) LIKE '%'
|| lower(:filter)
|| '%'
OR ( t.id IN (
SELECT DISTINCT
tt.tree_id
FROM
tree_tag tt, tag ttag
WHERE
tt.tag_id = ttag.id
AND lower(ttag.name) LIKE '%'
|| lower(:filter)
|| '%'
) ) );
SELECT
COUNT(*)
FROM
tree t
WHERE
t.farm_id = :farmid
AND ( lower(t.name) LIKE '%'
|| lower(:filter)
|| '%'
OR lower(t.description) LIKE '%'
|| lower(:filter)
|| '%'
OR ( t.id IN (
SELECT DISTINCT
tt.tree_id
FROM
tree_tag tt, tag ttag
WHERE
tt.tag_id = ttag.id
AND lower(ttag.name) LIKE '%'
|| lower(:filter)
|| '%'
) ) );
JPA -
void buildQuery(EntityManager em, String farmId, String filter) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tree> cq = cb.createQuery(Tree.class);
Root<Tree> tree = cq.from(Tree.class);
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(tree.get("farm").get("id"), farmId));
Predicate namePredicate = cb.like(tree.get(Tree_.name), "%"+filter+"%");
Predicate descriptionPredicate = cb.like(tree.get(Tree_.description), "%"+filter+"%");
Subquery<String> subQuery = cq.subquery(String.class);
Root<TreeTag> treeTagRoot = subQuery.from(TreeTag.class);
Join<TreeTag, Tag> treeTagTagJoin = treeTagRoot.join(TreeTag_.tag);
List<Predicate> subPredicates = new ArrayList<>();
subPredicates.add(cb.equal(treeTagRoot.get(TreeTag_.tag).get(Tag_.id), treeTagTagJoin.get(Tag_.id)));
subPredicates.add(cb.like(treeTagTagJoin.get(Tag_.name), "%"+filter+"%"));
subQuery.select(treeTagRoot.get(Treetag_.tree).get(Tree_.id)).distinct(true).where(subPredicates.toArray(new Predicate[0]));
Predicate inSubQueryPredicate = tree.get(Tree_.id).in(subQuery);
predicates.add(cb.or(namePredicate, descriptionPredicate, inSubQueryPredicate));
cq = cq.where(predicates.toArray(new Predicate[0]));
cq = cq.orderBy(request.buildOrderBy(cb, tree));
TypedQuery<Tree> query = em.createQuery(cq);
List<Tree> trees = query.getResultList();
// for count - using the same query as above but replacing above 2 lines as
// cq = cq.select(cb.count(tree));
// TypedQuery<Long> query = em.createQuery(cq);
// int count = query.getSingleResult().intValue();
}
Entity Classes -
#Entity
#Table(name = "FARM"))
public class Farm {
#Id
#Column(name = "ID")
String id;
public static final String NAME = "name";
public static final String DESCRIPTION = "description";
}
#Entity
#Table(name = "TAG", uniqueConstraints = #UniqueConstraint(columnNames = {"FARM_ID", "NAME"}))
public class Tag {
#Id
#Column(name = "ID")
String id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FARM_ID", nullable = false, updatable = false)
Farm farm;
#Column(name = "NAME")
protected String name;
#Column(name = "DESCRIPTION")
private String description;
#OneToMany(mappedBy = "tag", fetch = FetchType.LAZY)
#CascadeOnDelete
private List<TreeTag> treeTagList = new ArrayList<>();
}
#MappedSuperclass
public abstract class ResourceTag {
#Id
#Column(name = "ID")
String id;
#ManyToOne
#JoinColumn(name = "TAG_ID", nullable = false)
protected Tag tag;
#Column(name = "VALUE")
protected String value;
}
#Entity
#Table(name = "TREE_TAG", uniqueConstraints = #UniqueConstraint(columnNames = {"TAG_ID", "TREE_ID"}))
public class TreeTag extends ResourceTag {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "TREE_ID", nullable = false)
private Tree tree;
}
#Entity
#Table(name="TREE",uniqueConstraints = #UniqueConstraint(columnNames = {"NAME”,"FARM_ID"}))
public class Tree {
#Id
#Column(name = "ID")
String id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "FARM_ID", nullable = false)
Farm farm;
#Column(name= "NAME", nullable=false)
private String name;
#Column(name = "DESCRIPTION")
private String description;
#OneToMany(mappedBy = "tree", fetch = FetchType.LAZY)
#CascadeOnDelete
#BatchFetch(value=BatchFetchType.JOIN)
private List<TreeTag> treeTagList = new ArrayList<>();
}
How can I make this JPA code work for both the list and count? Thanks

Dart DateTime.toIso8601String() throwing exception when inserting into SQFlite database

I am having trouble inserting a DateTime.toIso8601String() into a SQLite (SQFlite) database in Dart. The issue I am having is with a property_model class, who's only job is to interact with the database and hold data. I have an almost identical address_model class that works how I expect it, but I am having trouble with the property_model. Whenever I try to call the Property.insert() method, I get this error:
E/flutter: [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Invalid argument: Instance of 'DateTime'
I don't have this issue with the very similar Address class though, and I am pretty stumped. Here are the property_model, address_model, and database files I am using (the database.dart file is a singleton that I use throughout the application).
property_model.dart
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
import 'package:villadex/model/database.dart' as db;
import 'package:villadex/model/address_model.dart';
class Property {
/// Constructors
Property({
required this.name,
required Address address,
required this.owner,
}) : _address = address,
_primaryKey = null,
_dateCreated = DateTime.now();
Property.existing(
{required this.name,
required Address address,
required this.owner,
required int? primaryKey,
required DateTime dateCreated})
: _address = address,
_primaryKey = primaryKey,
_dateCreated = dateCreated;
Property.fromJSON({required Map<String, dynamic> json})
: name = json['name'],
owner = json['owner'],
_address = Address.fromJson(json: json['location']),
_primaryKey = json['property_id'],
_dateCreated = DateTime.fromMillisecondsSinceEpoch(json['dateCreated']);
/// Data
String name;
String? owner;
final Address _address;
/*final List<Event> calendar;
final List<Expenditure> expenditures;
final List<Associate> associates;
final List<Earning> earnings;*/
final int? _primaryKey;
final DateTime _dateCreated;
///Methods
Future<void> insert() async {
String dateCreated = _dateCreated.toIso8601String().trim();
Map<String, dynamic> data = {
// SQFlite sets the primary key
'name': name,
'owner': owner,
'location': address.toJson(),
'dateCreated': dateCreated,
};
await db.DatabaseConnection.database.then((databaseConnection) => {
databaseConnection?.insert('properties', data,
conflictAlgorithm: ConflictAlgorithm.replace)
});
}
static Future<Property?> fetchById(int id) async {
String sql = "SELECT * FROM properties WHERE property_id = $id";
Future<List<Map<String, dynamic>>>? rawData;
await db.DatabaseConnection.database.then(
(databaseConnection) => {rawData = databaseConnection?.rawQuery(sql)});
return rawData?.then((data) {
return Property.fromJSON(json: data[0]);
});
}
/// Getters
Address get address => _address;
}
address_model.dart
import 'package:flutter/material.dart';
import 'package:villadex/model/database.dart' as db;
class Address {
/// Constructors
Address(
{required this.street1,
this.street2 = '',
required this.city,
this.state = '',
this.zip = '',
required this.country})
: _dateCreated = DateTime.now(),
_primaryKey = null,
_propertyId = null,
_associateId = null;
Address.existing({
required this.street1,
this.street2 = '',
required this.city,
this.state = '',
this.zip = '',
required this.country,
required DateTime dateCreated,
required int primaryKey,
int? propertyKey,
int? associateKey,
}) : _dateCreated = dateCreated,
_primaryKey = primaryKey,
_propertyId = propertyKey,
_associateId = associateKey;
Address.fromJson({required Map<String, dynamic> json})
: street1 = json['street1'],
street2 = json['street2'],
city = json['city'],
state = json['state'],
zip = json['zip'],
country = json['country'],
_primaryKey = json['address_id'],
_propertyId = json['property_id'],
_associateId = json['associate_id'],
_dateCreated = DateTime.parse(json['_dateCreated']);
/// Data
final String street1;
final String street2;
final String city;
final String state;
final String zip;
final String country;
final int? _primaryKey;
final int? _propertyId;
final int? _associateId;
final DateTime _dateCreated;
/// Methods
Future<void> insert() async {
Map<String, dynamic> data = {
// SQFlite sets the primaryKey
'property_id': _propertyId,
'associate_id': _associateId,
'dateCreated': _dateCreated.toIso8601String().trim(),
'street1': street1,
'street2': street2,
'city': city,
'zip': zip,
'country': country
};
await db.DatabaseConnection.database.then((databaseConnection) =>
{databaseConnection?.insert('addresses', data)});
}
// Returns an address by ID
static Future<Address?> fetchById(int id) async {
String sql = "SELECT * FROM addresses WHERE address_id = $id";
Future<List<Map<String, dynamic>>>? rawData;
await db.DatabaseConnection.database.then(
(databaseConnection) => {rawData = databaseConnection?.rawQuery(sql)});
return rawData?.then((data) {
return Address.fromJson(json: data[0]);
});
}
Map<String, dynamic> toJson() {
return {
'street1': street1,
'street2': street2,
'city': city,
'state': state,
'zip': zip,
'country': country,
'address_id': _primaryKey,
'property_id': _propertyId,
'associate_id': _associateId,
'dateCreated': _dateCreated
};
}
/// Getters
String get fullAddress =>
street1 +
" " +
street2 +
", " +
city +
" " +
state +
" " +
zip +
", " +
country;
DateTime get dateCreated => _dateCreated;
int get key => _primaryKey ?? 0;
/// Setters
}
database.dart
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:async';
import 'package:villadex/model/property_model.dart';
class DatabaseConnection {
//static final DatabaseConnection instance = DatabaseConnection.init();
//DatabaseConnection._init();
/// Database variable
static Database? _database;
/// Getter for the database
static Future<Database?> get database async {
// If _database is null, set it equal to the return value of _initDB
_database ??= await _initDB('database3');
return _database;
}
/// Initialize database
static Future<Database?> _initDB(String dbname) async {
final dbPath = await getApplicationDocumentsDirectory();
final path = join(dbPath.toString(), dbname);
var dbInstance = await openDatabase(path, version: 1, onCreate: _createDatabase);
return dbInstance;
}
/// Create the database
static Future _createDatabase(Database database, int version) async {
Batch batch = database.batch();
/// CREATE PROPERTIES TABLE
batch.execute('''CREATE TABLE properties(
property_id INTEGER PRIMARY KEY,
dateCreated TEXT NOT NULL,
name TEXT NOT NULL,
location TEXT NOT NULL,
owner TEXT NOT NULL,
calendar TEXT,
expenditures TEXT,
associates TEXT,
earnings TEXT
);''');
/// CREATE EXPENDITURES TABLE
batch.execute('''CREATE TABLE expenditures(
expenditure_id INTEGER PRIMARY KEY,
property_id INTEGER NOT NULL,
dateCreated TEXT NOT NULL,
name TEXT NOT NULL,
amount REAL NOT NULL,
numberUnits INTEGER NOT NULL,
isPaid INTEGER NOT NULL,
description TEXT,
category TEXT,
date TEXT,
associates TEXT,
FOREIGN KEY (property_id)
REFERENCES properties(property_id)
);''');
/// CREATE EARNINGS TABLE
batch.execute(''' CREATE TABLE earnings(
earning_id INTEGER PRIMARY KEY,
property_id INTEGER NOT NULL,
dateCreated TEXT NOT NULL,
name TEXT NOT NULL,
amount REAL NOT NULL,
description TEXT,
category TEXT,
date TEXT,
associates TEXT,
FOREIGN KEY (property_id)
REFERENCES properties(property_id)
);''');
/// CREATE CATEGORIES TABLE
batch.execute(''' CREATE TABLE categories(
category_id INTEGER NOT NULL,
dateCreated TEXT NOT NULL,
name TEXT NOT NULL
);''');
/// CREATE ASSOCIATES TABLE
batch.execute(''' CREATE TABLE associates(
associate_id INTEGER PRIMARY KEY,
property_id INTEGER NOT NULL,
dateCreated TEXT NOT NULL,
name TEXT NOT NULL,
contact TEXT,
role TEXT,
payments TEXT,
FOREIGN KEY (property_id)
REFERENCES properties (property_id)
);''');
/// CREATE CONTACTS TABLE
batch.execute(''' CREATE TABLE contact (
associate_id INTEGER NOT NULL,
phoneNumber TEXT,
email TEXT,
FOREIGN KEY (associate_id)
REFERENCES associates (associate_id)
);''');
/// CREATE ADDRESSES TABLE
batch.execute(''' CREATE TABLE addresses (
address_id INTEGER PRIMARY KEY,
property_id INTEGER,
associate_id INTEGER,
dateCreated TEXT NOT NULL,
street1 TEXT NOT NULL,
street2 TEXT,
city TEXT NOT NULL,
zip TEXT,
state TEXT,
country TEXT,
FOREIGN KEY (property_id)
REFERENCES properties (property_id),
FOREIGN KEY (associate_id)
REFERENCES associates (associate_id)
);''');
/// CREATE EVENT TABLE
batch.execute(''' CREATE TABLE event (
event_id INTEGER PRIMARY KEY,
property_id INTEGER NOT NULL,
dateCreated TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT,
address TEXT,
associates TEXT,
expenditures TEXT,
earnings TEXT,
FOREIGN KEY (property_id)
REFERENCES properties (property_id)
);''');
batch.commit();
}
Future close() async {
_database?.close;
}
}
Thank you for any help!
I figured it out. The address object within my property object did not turn its DateTime object into a string during the address.toJson() method, which is why it was giving me that error.

Hibernate unidirectional one-to-one mapping

I have the following table structure.
(The unique constraint is to avoid multiple 'details' to the same employee - I can't change the db structure).
https://i.ibb.co/r3pYQFj/fk.png
create table employee (
employee_id number(19),
salary number(10),
constraint pk_employee primary key (employee_id)
);
create table employee_details (
employee_details_id number(19),
employee_id number(19) not null,
address varchar2(256),
gender char(1),
constraint fk_employee foreign key (employee_id) references employee (employee_id),
constraint fk_employee_unq unique (employee_id)
);
Model class:
#Entity
#Table(name = "EMPLOYEE")
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "empgen")
#SequenceGenerator(name = "empgen", sequenceName = "SEQ_EMP", allocationSize = 1)
#Column(name = "EMPLOYEE_ID")
private Long id;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="EMPLOYEE_ID")
private EmployeeDetail empDetail;
...
#Entity
#Table(name = "EMPLOYEE_DETAIL")
public class EmployeeDetail implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "empdetgen")
#SequenceGenerator(name = "empdetgen", sequenceName = "SEQ_EMP_DET", allocationSize = 1)
#Column(name = "EMPLOYEE_DETAILS_ID")
private Long id;
#Column(name = "EMPLOYEE_ID")
private Long employeeId;
...
I have a rest controller that receive a JSON like this:
{
"employee": {
"salary": 80000,
"empDetail": {
"adress": "ST EXAMPLE",
"gender": "M"
}
}
}
I'm trying to persist all the entities via hibernate (5.4.28) saving Employee as the first entity, then with its primary key, EmployeeDetails using it's parent primary key but I get this:
java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("DCC"."EMPLOYEE_DETAILS"."EMPLOYEE_ID")
Why it's trying to save the child before the parent?
How the the class should be mapped?
I guess it's because Hibernate thinks that by putting #JoinColumn on Employee.empDetail, EMPLOYEE_ID belongs to the employee_details table.
Try putting creating a property EmployeeDetail.employee and put #JoinColumn on that one. If you need a reference from Employee, use #OneToOne(mappedBy=...).
How to use #JoinColumn
Activate logging to see which statements are executed

How to fix 'ERROR SqlExceptionHelper Column 'cardinalidadid' not found' error in jpa

I want to map an entity, which has a relationship with another table, but when mapping that relationship, it doesn't find the "Cardinality" column.
This is the code:
Entity elemento:
#Entity
#Table(name = "elemento")
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="clave", discriminatorType = DiscriminatorType.STRING, length=10)
public class Elemento implements Serializable, GenericInterface {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id")
private Integer id;
#Column(name = "clave", insertable = false)
private String clave;
#Column(name = "numero")
private String numero;
#Column(name = "nombre")
private String nombre;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Proyectoid")
private Proyecto proyecto;
#Column(name = "descripcion")
private String descripcion;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "EstadoElementoid", referencedColumnName="id")
private EstadoElemento estadoElemento;
Entity actor:
#Entity
#Table(name = "actor")
#Inheritance(strategy=InheritanceType.JOINED)
#PrimaryKeyJoinColumn(name = "Elementoid", referencedColumnName = "id")
#DiscriminatorValue("ACT")
public class Actor extends Elemento implements Serializable, GenericInterface, ElementoInterface {
private static final long serialVersionUID = 1L;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "Cardinalidadid", referencedColumnName = "id")
private Cardinalidad cardinalidad;
This is the description of the tables:
TABLE ACTOR:
| actor | CREATE TABLE `actor` (
`otraCardinalidad` varchar(45) DEFAULT NULL,
`Elementoid` int(11) NOT NULL,
`Cardinalidadid` int(11) NOT NULL,
PRIMARY KEY (`Elementoid`),
KEY `FKActor872913` (`Cardinalidadid`),
KEY `FKActor148309` (`Elementoid`),
CONSTRAINT `FKActor148309` FOREIGN KEY (`Elementoid`) REFERENCES
`elemento` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `FKActor872913` FOREIGN KEY (`Cardinalidadid`) REFERENCES
`cardinalidad` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
TABLE CARDINALIDAD
| cardinalidad | CREATE TABLE `cardinalidad` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nombre` varchar(10) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uniqueCardinalidad` (`nombre`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1 |
This error appears
02:57:43.663 [qtp1469821799-35] DEBUG
com.mchange.v2.c3p0.impl.NewPooledConnection -
com.mchange.v2.c3p0.impl.NewPooledConnection#48c2c8ce handling a
throwable. java.sql.SQLException: Column 'cardinalidadid' not found.

JPA - I get multiple rows instead of 1

I spent many hours to solve my problem but without success. I'd like to achieve something like this (but with ONE row instead of TWO):
My database:
CREATE TABLE odo.d_kryterium_naruszen (
id bigserial primary key,
kryterium text not null,
data_wpr TIMESTAMP not null DEFAULT clock_timestamp(),
opr bigint not null
);
CREATE TABLE odo.d_czynnik_naruszen (
id bigserial primary key,
czynnik text not null,
id_kryterium_naruszen bigint not null references odo.d_kryterium_naruszen(id),
stopien NUMERIC(10,2) not null,
data_wpr TIMESTAMP not null DEFAULT clock_timestamp(),
opr bigint not null
);
CREATE TABLE odo.d_dotkliwosc_naruszenia (
id bigserial primary key,
zakres numrange not null,
ocena text not null,
opis text not null,
wymagane_dzialanie text not null,
data_wpr TIMESTAMP not null DEFAULT clock_timestamp(),
opr bigint not null
);
CREATE TABLE odo.ocena_naruszenia_wynik (
id bigserial primary key,
wartosc_dotkliwosci_naruszenia NUMERIC(10,2) not null,
status_id bigint not null references odo.d_status_oceny_naruszenia(id),
ocena_naruszenia_id bigint not null references odo.ocena_naruszenia(id),
data_wpr TIMESTAMP not null DEFAULT clock_timestamp(),
opr bigint not null
);
create table odo.czynnik_naruszen_wynik(
id bigserial primary key,
ocena_naruszenia_wynik_id bigint not null references odo.ocena_naruszenia_wynik(id),
czynnik_naruszen_id bigint not null references odo.d_czynnik_naruszen(id),
komentarz text,
czynnik_wybrany boolean not null default false
wartosc_wybrana NUMERIC(10,2) not null,
data_wpr TIMESTAMP not null DEFAULT clock_timestamp(),
opr bigint not null
);
And here my entities:
#Data
#Entity
#Table(schema = "odo", name = "d_kryterium_naruszen")
public class ViolationCriterion extends BaseEntity {
#Column(name = "kryterium")
private String criterion;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "id_kryterium_naruszen")
private List<ViolationFactor> violationFactors;
}
#Data
#Entity
#Table(schema = "odo", name = "d_czynnik_naruszen")
public class ViolationFactor extends BaseEntity {
#Column(name = "czynnik")
private String factor;
#Column(name = "stopien")
private float degree;
#OneToMany
#JoinColumn(name = "czynnik_naruszen_id")
private List<IncidentAssessmentFactor> incidentAssessmentFactor;
}
#Data
#Entity
#Table(schema = "odo", name = "czynnik_naruszen_wynik")
public class IncidentAssessmentFactor extends BaseEntity {
#Column(name="komentarz")
private String comment;
#Column(name="czynnik_wybrany")
private Boolean factorIsSelected;
#Column(name = "wartosc_wybrana")
private Float value;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="ocena_naruszenia_wynik_id", updatable=false, insertable=false)
private IncidentAssessment incidentAssessment;
}
#Data
#Entity
#Table(schema = "odo", name = "ocena_naruszenia_wynik")
public class IncidentAssessment extends BaseEntity {
#Column(name="ocena_naruszenia_id")
private Long incidentAssessmentId;
#Column(name = "wartosc_dotkliwosci_naruszenia")
private Float severityDegreeValue;
My repository:
#Repository
public interface ViolationCriterionRepository extends JpaRepository<ViolationCriterion, Long> {
// #Query("select vc from ViolationCriterion vc inner join vc.violationFactors vf inner join vf.incidentAssessmentFactor iaf inner join iaf.incidentAssessment ia where ia.incidentAssessmentId = ?1 group by vc ")
#Query("select vc from ViolationCriterion vc inner join vc.violationFactors vf inner join vf.incidentAssessmentFactor iaf inner join iaf.incidentAssessment ia where ia.incidentAssessmentId = ?1 group by vc ")
// #Query(value = "select kn.kryterium from odo.d_kryterium_naruszen kn join odo.d_czynnik_naruszen cn on kn.id = cn.id_kryterium_naruszen join odo.czynnik_naruszen_wynik cnw on cnw.czynnik_naruszen_id = cn.id join odo.ocena_naruszenia_wynik onw on cnw.ocena_naruszenia_wynik_id = onw.id where onw.ocena_naruszenia_id = ?1 group by kn.id, cn.id, cnw.id, onw.id", nativeQuery = true)
// #Query(value = "select kn.id, kn.kryterium, kn.data_wpr, kn.opr, cn.id, cn.czynnik, cn.stopien, cn.opr, cn.data_wpr, cnw.id, cnw.data_wpr, cnw.opr, cnw.komentarz, cnw.czynnik_wybrany, cnw.wartosc_wybrana, onw.id, onw.data_wpr, onw.opr, onw.ocena_naruszenia_id, onw.wartosc_dotkliwosci_naruszenia from odo.d_kryterium_naruszen kn join odo.d_czynnik_naruszen cn on kn.id = cn.id_kryterium_naruszen join odo.czynnik_naruszen_wynik cnw on cnw.czynnik_naruszen_id = cn.id join odo.ocena_naruszenia_wynik onw on cnw.ocena_naruszenia_wynik_id = onw.id where onw.ocena_naruszenia_id = ?1 group by kn.id, cn.id, cnw.id, onw.id", nativeQuery = true)
List<ViolationCriterion> findIncidentAssessmentByIncidentAssessmentId(Long incidentId);
// List<ViolationCriterion> findByViolationFactorsIncidentAssessmentFactorIncidentAssessmentIncidentAssessmentIdGroupByViolationCriterionCriterion(Long id);
}
And here I call my repository:
List<ViolationCriterion> violationCriteria = violationCriterionRepository.findIncidentAssessmentByIncidentAssessmentId(id);//vi
In a table czynnik_naruszen_wynik I have 2 different rows because I have 2 rows in table ocena_naruszenia_wynik. The problem is that I have multiple values of entity IncidentAssessmentFactor instead of 1