Ich portiere eine mittelgroße CRUD-Anwendung von .Net nach Qt und bin auf der Suche nach einem Muster für die Erstellung von Persistenzklassen. In .Net habe ich in der Regel abstrakte Persistenzklasse mit grundlegenden Methoden (einfügen, aktualisieren, löschen, auswählen) zum Beispiel erstellt:
public class DAOBase<T>
{
public T GetByPrimaryKey(object primaryKey) {...}
public void DeleteByPrimaryKey(object primaryKey) {...}
public List<T> GetByField(string fieldName, object value) {...}
public void Insert(T dto) {...}
public void Update(T dto) {...}
}
Dann habe ich es für bestimmte Tabellen/DTOs unterklassifiziert und Attribute für das DB-Tabellenlayout hinzugefügt:
[DBTable("note", "note_id", NpgsqlTypes.NpgsqlDbType.Integer)]
[DbField("note_id", NpgsqlTypes.NpgsqlDbType.Integer, "NoteId")]
[DbField("client_id", NpgsqlTypes.NpgsqlDbType.Integer, "ClientId")]
[DbField("title", NpgsqlTypes.NpgsqlDbType.Text, "Title", "")]
[DbField("body", NpgsqlTypes.NpgsqlDbType.Text, "Body", "")]
[DbField("date_added", NpgsqlTypes.NpgsqlDbType.Date, "DateAdded")]
class NoteDAO : DAOBase<NoteDTO>
{
}
Dank des .Net-Reflection-Systems konnte ich eine starke Wiederverwendung von Code und eine einfache Erstellung neuer ORMs erreichen.
Der einfachste Weg, diese Art von Dingen in Qt zu tun, scheint die Verwendung von Modellklassen aus dem QtSql-Modul zu sein. Leider, in meinem Fall bieten sie eine zu abstrakte Schnittstelle. Ich brauche zumindest Transaktionsunterstützung und Kontrolle über einzelne Commits, die QSqlTableModel nicht bietet.
Könnten Sie mir einige Hinweise zur Lösung dieses Problems mit Qt geben oder mich auf einige Referenzmaterialien verweisen?
Aktualisierung:
Basierend auf Haralds Hinweisen habe ich eine Lösung implementiert, die den obigen .Net-Klassen sehr ähnlich ist. Jetzt habe ich zwei Klassen.
UniversalDAO das erbt QObject und beschäftigt sich mit QObject DTOs mit Metatyp-System:
class UniversalDAO : public QObject
{
Q_OBJECT
public:
UniversalDAO(QSqlDatabase dataBase, QObject *parent = 0);
virtual ~UniversalDAO();
void insert(const QObject &dto);
void update(const QObject &dto);
void remove(const QObject &dto);
void getByPrimaryKey(QObject &dto, const QVariant &key);
};
Und eine generische SpezialisierteDAO die die Daten von UniversalDAO zum entsprechenden Typ:
template<class DTO>
class SpecializedDAO
{
public:
SpecializedDAO(UniversalDAO *universalDao)
virtual ~SpecializedDAO() {}
DTO defaultDto() const { return DTO; }
void insert(DTO dto) { dao->insert(dto); }
void update(DTO dto) { dao->update(dto); }
void remove(DTO dto) { dao->remove(dto); }
DTO getByPrimaryKey(const QVariant &key);
};
Unter Verwendung der oben genannten, deklariere ich die konkrete DAO-Klasse wie folgt:
class ClientDAO : public QObject, public SpecializedDAO<ClientDTO>
{
Q_OBJECT
public:
ClientDAO(UniversalDAO *dao, QObject *parent = 0) :
QObject(parent), SpecializedDAO<ClientDTO>(dao)
{}
};
Von innen ClientDAO Ich muss einige Datenbankinformationen einstellen für UniversalDAO . Das ist der Punkt, an dem meine Umsetzung hässlich wird, weil ich es so mache:
QMap<QString, QString> fieldMapper;
fieldMapper["client_id"] = "clientId";
fieldMapper["name"] = "firstName";
/* ...all column <-> field pairs in here... */
dao->setFieldMapper(fieldMapper);
dao->setTable("client");
dao->setPrimaryKey("client_id");
Ich tue es im Konstruktor, so dass es für jemanden, der den Header durchsucht, nicht auf den ersten Blick sichtbar ist. In der .Net-Version war es leicht zu erkennen und zu verstehen.
Haben Sie ein paar Ideen, wie ich es besser machen kann?