@aceitadev/adatabase 0.3.0 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,66 +1,171 @@
1
- ```markdown
2
- # aMySQL - Node.js (TypeScript) port
3
-
4
- Esta é uma implementação inicial do seu sistema aMySQL para Node.js usando TypeScript e decorators.
5
-
6
- Principais características
7
- - Pool de conexões (mysql2/promise)
8
- - Decorators: @Table, @Column, @Id, @Nullable
9
- - ColumnAdapter para tipos serializados customizados
10
- - ActiveRecord com save/update/delete
11
- - QueryBuilder (where/order/limit)
12
- - SchemaManager para criar/atualizar colunas automaticamente
13
-
14
- Como usar (exemplo rápido)
15
-
16
- 1) Instale dependências
17
- npm install
18
-
19
- 2) Configure tsconfig (já incluído) e habilite experimentalDecorators e emitDecoratorMetadata.
20
-
21
- 3) Exemplo de modelo (src/example.ts)
22
- ```ts
23
- import "reflect-metadata";
24
- import { init } from "./MySQL";
25
- import { Table } from "./decorators/Table";
26
- import { Column } from "./decorators/Column";
27
- import { Id } from "./decorators/Id";
28
- import { ActiveRecord } from "./ActiveRecord";
29
- import { SchemaManager } from "./SchemaManager";
30
-
31
- @Table("users")
32
- class User extends ActiveRecord {
33
- @Id()
34
- id!: number;
35
-
36
- @Column({ limit: 150 })
37
- name!: string;
38
-
39
- @Column()
40
- email!: string;
41
- }
42
-
43
- async function main() {
44
- await init({ host: "127.0.0.1", database: "test", user: "root", password: "" });
45
- const sm = new SchemaManager([User]);
46
- await sm.migrate();
47
-
48
- const u = new User();
49
- u.name = "Fulano";
50
- u.email = "fulano@example.com";
51
- await u.save();
52
-
53
- const fetched = await User.find().where("email", "=", "fulano@example.com").first();
54
- console.log(fetched);
55
- }
56
-
57
- main().catch(console.error);
58
- ```
59
-
60
- Limitações & próximos passos
61
- - QueryBuilder suporta nomes de campos como strings; não foi implementado introspecção de lambda getter como no Java.
62
- - Adapters: você fornece uma classe com métodos serialize/deserialize para o Column decorator.
63
- - Migração: apenas cria tabelas e adiciona colunas que faltam; não altera ou remove colunas existentes.
64
- - Recomendado: adicionar testes, melhorar mapeamento de tipos (enums, UUID), e adicionar caching e transações.
65
-
66
- ```
1
+ # aDatabase ORM
2
+
3
+ aDatabase is a lightweight and easy-to-use ORM (Object-Relational Mapping) for TypeScript, inspired by the Active Record pattern. It allows you to interact with your database using object-oriented syntax, abstracting away the need to write raw SQL queries for most common operations.
4
+
5
+ ## Features
6
+
7
+ * **Active Record Pattern**: Models represent database tables and instances represent rows.
8
+ * **Decorator-based Schema**: Define your database schema using simple decorators.
9
+ * **Multi-database Support**: Supports both MySQL and PostgreSQL.
10
+ * **Query Builder**: A fluent API for building complex queries.
11
+ * **Schema Migration**: Keep your database schema in sync with your models.
12
+ * **Relationships**: Supports `HasOne`, `HasMany`, and `BelongsTo` relationships.
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @aceitadev/adatabase
18
+ ```
19
+
20
+ ## Configuration
21
+
22
+ First, you need to initialize the database connection. This should be done once when your application starts.
23
+
24
+ ```typescript
25
+ import { init } from '@aceitadev/adatabase';
26
+
27
+ init('mysql', {
28
+ host: 'localhost',
29
+ user: 'root',
30
+ password: 'password',
31
+ database: 'my_database'
32
+ });
33
+ ```
34
+
35
+ Supported database types are `'mysql'` and `'postgres'`.
36
+
37
+ ## Defining Models
38
+
39
+ Models are defined as classes that extend `ActiveRecord`. You use decorators to map the class and its properties to a database table and its columns.
40
+
41
+ ```typescript
42
+ import { ActiveRecord, Table, Id, Column } from '@aceitadev/adatabase';
43
+
44
+ @Table('users')
45
+ export class User extends ActiveRecord {
46
+ @Id()
47
+ id: number;
48
+
49
+ @Column({ type: String })
50
+ name: string;
51
+
52
+ @Column({ type: String, unique: true })
53
+ email: string;
54
+ }
55
+ ```
56
+
57
+ ## CRUD Operations
58
+
59
+ ### Creating and Saving Records
60
+
61
+ To create a new record, instantiate a model and call the `save()` method.
62
+
63
+ ```typescript
64
+ const user = new User();
65
+ user.name = 'John Doe';
66
+ user.email = 'john.doe@example.com';
67
+ await user.save();
68
+ ```
69
+
70
+ ### Finding Records
71
+
72
+ Use the static `find()` method to get a `QueryBuilder` instance.
73
+
74
+ **Find all users:**
75
+
76
+ ```typescript
77
+ const users = await User.find().get();
78
+ ```
79
+
80
+ **Find a single user by ID:**
81
+
82
+ ```typescript
83
+ const user = await User.find().where('id', '=', 1).first();
84
+ ```
85
+
86
+ **Find users with a specific condition:**
87
+
88
+ ```typescript
89
+ const users = await User.find().where('name', 'LIKE', 'John%').get();
90
+ ```
91
+
92
+ ### Updating Records
93
+
94
+ To update a record, modify its properties and call `save()` again.
95
+
96
+ ```typescript
97
+ const user = await User.find().where('id', '=', 1).first();
98
+ if (user) {
99
+ user.name = 'Jane Doe';
100
+ await user.save();
101
+ }
102
+ ```
103
+
104
+ ### Deleting Records
105
+
106
+ To delete a record, call the `delete()` method on a model instance.
107
+
108
+ ```typescript
109
+ const user = await User.find().where('id', '=', 1).first();
110
+ if (user) {
111
+ await user.delete();
112
+ }
113
+ ```
114
+
115
+ ## Relationships
116
+
117
+ You can define relationships between your models using the `@BelongsTo`, `@HasMany`, and `@HasOne` decorators.
118
+
119
+ ```typescript
120
+ import { ActiveRecord, Table, Id, Column, BelongsTo, HasMany } from '@aceitadev/adatabase';
121
+
122
+ @Table('posts')
123
+ export class Post extends ActiveRecord {
124
+ @Id()
125
+ id: number;
126
+
127
+ @Column({ type: String })
128
+ title: string;
129
+
130
+ @Column({ type: Number })
131
+ userId: number;
132
+
133
+ @BelongsTo(() => User, { foreignKey: 'userId' })
134
+ user: User;
135
+ }
136
+
137
+ @Table('users')
138
+ export class User extends ActiveRecord {
139
+ @Id()
140
+ id: number;
141
+
142
+ @Column({ type: String })
143
+ name: string;
144
+
145
+ @HasMany(() => Post, { foreignKey: 'userId' })
146
+ posts: Post[];
147
+ }
148
+ ```
149
+
150
+ To eager-load relationships, use the `include()` method in the `QueryBuilder`.
151
+
152
+ ```typescript
153
+ const userWithPosts = await User.find().where('id', '=', 1).include(Post).first();
154
+ console.log(userWithPosts.posts);
155
+ ```
156
+
157
+ ## Schema Migration
158
+
159
+ The `SchemaManager` helps you keep your database schema in sync with your models.
160
+
161
+ ```typescript
162
+ import { SchemaManager } from '@aceitadev/adatabase';
163
+ import { User, Post } from './models'; // Import your models
164
+
165
+ const schemaManager = new SchemaManager([User, Post]);
166
+ await schemaManager.migrate();
167
+ ```
168
+
169
+ When you run `migrate()`, the `SchemaManager` will:
170
+ * Create tables that don't exist.
171
+ * Add columns that are missing from existing tables.
@@ -16,6 +16,7 @@ export declare class QueryBuilder<T extends ActiveRecord> {
16
16
  limit(count: number): this;
17
17
  offset(count: number): this;
18
18
  first(): Promise<T | null>;
19
+ count(): Promise<number>;
19
20
  get(): Promise<T[]>;
20
21
  private getColumnName;
21
22
  private mapRowsToEntities;
@@ -63,6 +63,47 @@ class QueryBuilder {
63
63
  return rows.length > 0 ? rows[0] : null;
64
64
  });
65
65
  }
66
+ count() {
67
+ return __awaiter(this, void 0, void 0, function* () {
68
+ const table = (0, Table_1.getTableName)(this.model);
69
+ if (!table) {
70
+ throw new PersistenceException_1.PersistenceException("Model has no @Table", null);
71
+ }
72
+ const columnsMeta = (0, Column_1.getColumnMeta)(this.model);
73
+ if (!columnsMeta) {
74
+ throw new PersistenceException_1.PersistenceException("Model has no @Column decorators", null);
75
+ }
76
+ const allowedOperators = ['=', '!=', '<>', '>', '<', '>=', '<=', 'LIKE', 'IN', 'IS NULL', 'IS NOT NULL'];
77
+ const params = [];
78
+ const baseAlias = 't1';
79
+ let sql = `SELECT COUNT(*) as count FROM \`${table}\` AS ${baseAlias}`;
80
+ if (this.whereClauses.length > 0) {
81
+ sql += " WHERE ";
82
+ this.whereClauses.forEach((c, i) => {
83
+ if (!allowedOperators.includes(c.operator.toUpperCase())) {
84
+ throw new Error(`Invalid operator used: ${c.operator}`);
85
+ }
86
+ if (i > 0) {
87
+ sql += ` ${c.booleanOp} `;
88
+ }
89
+ const colName = this.getColumnName(c.field, columnsMeta);
90
+ sql += `\`${baseAlias}\`.\`${colName}\` ${c.operator} ?`;
91
+ params.push(c.value);
92
+ });
93
+ }
94
+ try {
95
+ const rows = yield (0, Database_1.query)(sql, params);
96
+ if (rows && rows.length > 0 && rows[0].count !== undefined) {
97
+ return Number(rows[0].count);
98
+ }
99
+ return 0;
100
+ }
101
+ catch (error) {
102
+ console.error("Failed to execute count query:", error);
103
+ throw new PersistenceException_1.PersistenceException("Failed to count records");
104
+ }
105
+ });
106
+ }
66
107
  get() {
67
108
  return __awaiter(this, void 0, void 0, function* () {
68
109
  const table = (0, Table_1.getTableName)(this.model);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aceitadev/adatabase",
3
- "version": "0.3.0",
3
+ "version": "0.3.6",
4
4
  "description": "Uma biblioteca para facilitar a interação com bancos de dados MySQL e PostgreSQL em projetos TypeScript/Node.js.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",