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.
Related
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)
);
I'm having problem with foreign key while adding a new habit and inserting it's data in db and I don't really know why, because everything seems right. the error is:
[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: DatabaseException(FOREIGN KEY constraint failed (code 787)) sql 'INSERT OR FAIL INTO Habits (userId, text, emoji, period, startPeriod) VALUES (?, ?, ?, ?, NULL)' args [1, habit, ☺, [1,2,3,4,5,6,7]]
error accrued here:
class DataService {...
late final Database db;
Future<void> addHabit(Habit newHabit) async {
final newHabitId = await HabitsDb.createHabit(db, newHabit);//error accrued here
final days = (await HabitsDb.getAllDays(db)).map((e) => Day.fromDb(e, []));
for (final day in days) {
if (newHabit.startPeriod != null &&
newHabit.startPeriod!.compareTo(day.date) > 0) {
continue;
}
if (newHabit.period.contains(day.date.weekday)) {
await HabitsDb.createHabitDay(
db, day.date.millisecondsSinceEpoch, newHabitId);
}
}
await _reloadDaysInCache();
_loadHabits();
} ...}
then:
class HabitsDb{...
static Future<int> createHabit(Database db, Habit habit) async {
return db.insert(HabitsTable.tableName, await habit.toDb(),
conflictAlgorithm: ConflictAlgorithm.fail);
}...}
model:
class Habit {
late final int id;
late final int userId;
late String text;
late String emoji;
late final List<int> period;
late final DateTime? startPeriod;
...
Future<Map<String, dynamic>> toDb() async {
print(text);
print(userId);
return {
//HabitsTable.id: id,
HabitsTable.userId: userId,
HabitsTable.text: text,
HabitsTable.emoji: emoji,
HabitsTable.period: jsonEncode(period),
HabitsTable.startPeriod: startPeriod?.millisecondsSinceEpoch
};
}...}
creating table:
class HabitsTable {
static const String tableName = 'Habits';
static const String id = 'id';
static const String userId = 'userId';
static const String text = 'text';
static const String emoji = 'emoji';
static const String period = 'period';
static const String startPeriod = 'startPeriod';
static const String createQuery = '''
CREATE TABLE IF NOT EXISTS $tableName (
$id INTEGER PRIMARY KEY AUTOINCREMENT,
$userId INTEGER NOT NULL,
$text TEXT NOT NULL,
$emoji TEXT NOT NULL,
$period TEXT NOT NULL,
$startPeriod INTEGER,
FOREIGN KEY($userId) REFERENCES ${UsersTable.tableName}(id));''';
}
if more code is needed you can check https://github.com/sarasoltan/habit_tracker
plz help if you know how to solve this error.
I have an issue with userId in Habits table. it is a foreign key from id in Users Table. but I keep getting the error "Unhandled Exception: Null check operator used on a null value " from AddHabitDialogController class.
obviously userId should never be null. where is the issue? how can I solve it?
if more code is needed you can check : https://github.com/sarasoltan/habit_tracker
Users Table:
class UsersTable {
static const String tableName = 'Users';
static const String id = 'id';
static const String email = 'email';
static const String createQuery = '''
CREATE TABLE IF NOT EXISTS $tableName (
$id integer primary key autoincrement,
$email text not null unique);''';
#override
String toString() => 'Person, ID: $id, email: $email';
#override
bool operator ==(covariant Users other) => id == other.id;
#override
int get hashCode => id.hashCode;
}
Habits table:
class HabitsTable {
static const String tableName = 'Habits';
static const String id = 'id';
static const String userId = 'userId';
static const String text = 'text';
static const String emoji = 'emoji';
static const String period = 'period';
static const String startPeriod = 'startPeriod';
static const String createQuery = '''
CREATE TABLE IF NOT EXISTS $tableName (
$id integer primary key autoincrement,
$userId integer not null,
$text text not null,
$emoji text not null,
$period text not null,
$startPeriod integer,
FOREIGN Key($userId) REFERENCES ${UsersTable.tableName}(${UsersTable.id}));''';
User model class:
class Users {
late final int id;
late String email;
Users({
required this.id,
required this.email,
});
Users.fromDb(Map<String, dynamic> map) {
id = map[UsersTable.id];
email = map[UsersTable.email];
}
}
Habit model class:
class Habit {
late final int id;
late final int userId;
late String text;
late String emoji;
late final List<int> period;
late final DateTime? startPeriod;
Habit(
{
//required this.id,
required this.userId,
required this.text,
required this.emoji,
required this.period,
this.startPeriod});
Habit.fromDb(Map<String, dynamic> map) {
id = map[HabitsTable.id] as int;
userId = map[HabitsTable.userId] as int;
text = map[HabitsTable.text] as String;
emoji = map[HabitsTable.emoji];
period = (jsonDecode(map[HabitsTable.period]) as List<dynamic>)
.map((e) => e as int)
.toList();
if (map[HabitsTable.startPeriod] != null) {
startPeriod =
DateTime.fromMillisecondsSinceEpoch(map[HabitsTable.startPeriod]);
} else {
startPeriod = null;
}
}
Map<String, dynamic> toDb() {
Users? owner;
return {
//HabitsTable.id: id,
HabitsTable.userId: owner!.id,
HabitsTable.text: text,
HabitsTable.emoji: emoji,
HabitsTable.period: jsonEncode(period),
HabitsTable.startPeriod: startPeriod?.millisecondsSinceEpoch
};
}
AddHabitDialogController(error):
class AddHabitDialogController {
Users? owner;
//final user = FirebaseAuth.instance.currentUser;
//String get owneruserId => AuthService.firebase().currentUser!.id;
final List<bool> period = [true, true, true, true, true, true, true];
int? id;
int? userId;
String? emoji;
String? text;
StartPeriod startPeriod = StartPeriod.none;
final StreamController<bool> _addBtnEnabledCtrl = StreamController();
Stream<bool> get addBtnEnabled => _addBtnEnabledCtrl.stream;
final StreamController<StartPeriod> _selectedStartPeriodCtrl =
StreamController();
Stream<StartPeriod> get selectedStartPeriod =>
_selectedStartPeriodCtrl.stream;
final StreamController<bool> _loadingCtrl = StreamController();
Stream<bool> get loading => _loadingCtrl.stream;
void changePeriodValue(int index, bool newValue) {
period[index] = newValue;
_updateAddBtnEnabledState();
}
void changeTextValue(String newValue) {
text = newValue;
_updateAddBtnEnabledState();
}
void changeEmojiValue(String newEmoji) {
emoji = newEmoji;
_updateAddBtnEnabledState();
}
void changeStartPeriod(StartPeriod newValue) {
startPeriod = newValue;
_selectedStartPeriodCtrl.add(startPeriod);
}
Future<void> addHabit(BuildContext context) async {
_loadingCtrl.add(true);
final dataService = GetIt.I.get<DataService>();
final List<int> forPeriod = [];
for (int i = 0; i < period.length; i++) {
if (period[i]) {
forPeriod.add(i + 1);
}
}
final habit = Habit(
//id: id!,
userId: owner!.id, //error from here
emoji: emoji!,
text: text!,
period: forPeriod,
startPeriod: _calculateStartPeriodDateTime());
await dataService.addHabit(habit);
_loadingCtrl.add(false);
Navigator.of(context).pop();
}
void _updateAddBtnEnabledState() {
_addBtnEnabledCtrl.add((text?.isNotEmpty ?? false) &&
(emoji?.isNotEmpty ?? false) &&
period.where((e) => e).isNotEmpty);
}
DateTime? _calculateStartPeriodDateTime() {
final now = DateTime.now();
switch (startPeriod) {
case StartPeriod.today:
return DateTime(now.year, now.month, now.day);
case StartPeriod.thisMonth:
return DateTime(now.year, now.month);
case StartPeriod.thisYear:
return DateTime(now.year);
case StartPeriod.none:
default:
return null;
}
}
void dispose() {
_addBtnEnabledCtrl.close();
_loadingCtrl.close();
_selectedStartPeriodCtrl.close();
}
}
enum StartPeriod { none, today, thisMonth, thisYear }
Because the local variable of AddHabitDialogController owner can't be referenced before it is declared
class AddHabitDialogController {
Users? owner; // -> you did not assign this owner?
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
I have this MariaDB table which I would like to use for bar chart:
CREATE TABLE `payment_transaction_daily_facts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date` date DEFAULT NULL,
`year` int(11) DEFAULT NULL,
`month` int(11) DEFAULT NULL,
`week` int(11) DEFAULT NULL,
`day` int(11) DEFAULT NULL,
`volume` int(11) DEFAULT NULL,
`count` int(11) DEFAULT NULL,
'created_at' date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
In my example SQL query I have single column for Date. How I can calculate the volumes per day for last 10 days when I have split date, year, month, week and day into different columns?
The final result should be for example:
Date | Amount| Number of transactions per day |
11-11-2018 | 30 | 3 |
11-12-2018 | 230 | 13 |
I tried this:
SELECT SUM(amount) AS sum_volume, COUNT(*) AS sum_Transactions
WHERE (created_at BETWEEN '2018-11-07' AND '2018-11-08')
GROUP BY DATE(created_at)
I want to return the generated data using DTO:
public class DashboardDTO {
private Date date;
private int sum_volume;
private int sum_Transactions;
... getters and setters
}
Rest controller:
#RestController
#RequestMapping("/dashboard")
public class DashboardController {
private static final Logger LOG = LoggerFactory.getLogger(DashboardController.class);
#Autowired
private DashboardRepository dashboardRepository;
#Autowired
private PaymentTransactionsDailyFactsMapper mapper;
#GetMapping("/volumes")
public ResponseEntity<List<DashboardDTO>> getProcessingVolumes(#PathVariable String start_date, #PathVariable String end_date) {
List<DashboardDTO> list = StreamSupport.stream(dashboardRepository.findPaymentTransactionsDailyFacts(start_date, end_date).spliterator(), false)
.map(mapper::toDTO)
.collect(Collectors.toList());
return ResponseEntity.ok(list);
}
}
JPA query:
public List<PaymentTransactionsDailyFacts> findPaymentTransactionsDailyFacts(LocalDateTime start_date, LocalDateTime end_date) {
String hql = "SELECT SUM(amount) AS sum_volume, COUNT(*) AS sum_Transactions " +
" WHERE (created_at BETWEEN :start_date AND :end_date )" +
" GROUP BY DATE(created_at)";
TypedQuery<PaymentTransactionsDailyFacts> query = entityManager.createQuery(hql,
PaymentTransactionsDailyFacts.class).setParameter("start_date", start_date).setParameter("end_date", end_date);
List<PaymentTransactionsDailyFacts> data = query.getResultList();
return data;
}
How should I implement the query properly?
When I receive start_date and end_date as String from Angular how should I convert it into LocaDateTime?
Well, as I commented, time is a dimension in a data warehouse star schema, and I guess period is as well. So you should have two dimension tables, a TimeDim for LocalDate, and a PeriodDim for Period. Then you should have a Fact with the an embeddedId made up of the various dimensions in your schema. Then you would have facts for 1 day periods and facts for 10 day periods. If you insisted on summing facts you have the issue that JPA cannot do a <= or >= comparison against composite keys. Since you are only summing 10 days you could use a in clause to select 10 keys, but again, you should have facts for the periods you need.
#Entity
public class TimeDim {
#Id
private LocalDate localDate;
#Entity
public class PeriodDim {
#Id
private Period period;
// need this too
#Converter(autoApply = true)
public class LocalDateAttributeConverter implements AttributeConverter<LocalDate, Date> {
#Override
public Date convertToDatabaseColumn(LocalDate locDate) {
return (locDate == null ? null : Date.valueOf(locDate));
}
#Override
public LocalDate convertToEntityAttribute(Date sqlDate) {
return (sqlDate == null ? null : sqlDate.toLocalDate());
}
}
#SuppressWarnings("serial")
#Embeddable
public class DimKey implements Serializable {
private LocalDate localDate;
private Period period;
#Entity
public class Fact {
#EmbeddedId
private DimKey dimKey = new DimKey();
private long amount;
And for example:
tx.begin();
TimeDim td10 = new TimeDim();
td10.setLocalDate(LocalDate.now().minusDays(5));
em.persist(td10);
TimeDim td5 = new TimeDim();
td5.setLocalDate(LocalDate.now().minusDays(10));
em.persist(td5);
PeriodDim pd5 = new PeriodDim();
pd5.setPeriod(Period.ofDays(5));
em.persist(pd5);
PeriodDim pd10 = new PeriodDim();
pd10.setPeriod(Period.ofDays(10));
em.persist(pd10);
Fact f10 = new Fact();
f10.getDimKey().setLocalDate(td10.getLocalDate());
f10.getDimKey().setPeriod(pd10.getPeriod());
f10.setAmount(100);
em.persist(f10);
Fact f51 = new Fact();
f51.getDimKey().setLocalDate(td10.getLocalDate());
f51.getDimKey().setPeriod(pd5.getPeriod());
f51.setAmount(50);
em.persist(f51);
Fact f52 = new Fact();
f52.getDimKey().setLocalDate(td5.getLocalDate());
f52.getDimKey().setPeriod(pd5.getPeriod());
f52.setAmount(50);
em.persist(f52);
tx.commit();
em.clear();
DimKey dk = new DimKey();
dk.setLocalDate(td10.getLocalDate());
dk.setPeriod(pd10.getPeriod());
Fact f = em.createQuery("select f from Fact f where f.dimKey = :dimKey", Fact.class)
.setParameter("dimKey", dk)
.getSingleResult();
System.out.println("From 10 day period: " + f.getAmount());
DimKey dk1 = new DimKey();
dk1.setLocalDate(td10.getLocalDate());
dk1.setPeriod(pd5.getPeriod());
DimKey dk2 = new DimKey();
dk2.setLocalDate(td5.getLocalDate());
dk2.setPeriod(pd5.getPeriod());
Long sum = em.createQuery("select sum(f.amount) from Fact f where f.dimKey in (:dimKey1 , :dimKey2)", Long.class)
.setParameter("dimKey1", dk1)
.setParameter("dimKey2", dk2)
.getSingleResult();
System.out.println("From 2*5 day period: " + sum);