sqlalchemy-jdbcapi 2.0.0.post2__py3-none-any.whl

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.
Files changed (36) hide show
  1. sqlalchemy_jdbcapi/__init__.py +128 -0
  2. sqlalchemy_jdbcapi/_version.py +34 -0
  3. sqlalchemy_jdbcapi/dialects/__init__.py +30 -0
  4. sqlalchemy_jdbcapi/dialects/base.py +879 -0
  5. sqlalchemy_jdbcapi/dialects/db2.py +134 -0
  6. sqlalchemy_jdbcapi/dialects/mssql.py +117 -0
  7. sqlalchemy_jdbcapi/dialects/mysql.py +152 -0
  8. sqlalchemy_jdbcapi/dialects/oceanbase.py +218 -0
  9. sqlalchemy_jdbcapi/dialects/odbc_base.py +389 -0
  10. sqlalchemy_jdbcapi/dialects/odbc_mssql.py +69 -0
  11. sqlalchemy_jdbcapi/dialects/odbc_mysql.py +101 -0
  12. sqlalchemy_jdbcapi/dialects/odbc_oracle.py +80 -0
  13. sqlalchemy_jdbcapi/dialects/odbc_postgresql.py +63 -0
  14. sqlalchemy_jdbcapi/dialects/oracle.py +180 -0
  15. sqlalchemy_jdbcapi/dialects/postgresql.py +110 -0
  16. sqlalchemy_jdbcapi/dialects/sqlite.py +141 -0
  17. sqlalchemy_jdbcapi/jdbc/__init__.py +98 -0
  18. sqlalchemy_jdbcapi/jdbc/connection.py +244 -0
  19. sqlalchemy_jdbcapi/jdbc/cursor.py +329 -0
  20. sqlalchemy_jdbcapi/jdbc/dataframe.py +198 -0
  21. sqlalchemy_jdbcapi/jdbc/driver_manager.py +353 -0
  22. sqlalchemy_jdbcapi/jdbc/exceptions.py +53 -0
  23. sqlalchemy_jdbcapi/jdbc/jvm.py +176 -0
  24. sqlalchemy_jdbcapi/jdbc/type_converter.py +292 -0
  25. sqlalchemy_jdbcapi/jdbc/types.py +72 -0
  26. sqlalchemy_jdbcapi/odbc/__init__.py +46 -0
  27. sqlalchemy_jdbcapi/odbc/connection.py +136 -0
  28. sqlalchemy_jdbcapi/odbc/exceptions.py +48 -0
  29. sqlalchemy_jdbcapi/py.typed +2 -0
  30. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/METADATA +825 -0
  31. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/RECORD +36 -0
  32. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/WHEEL +5 -0
  33. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/entry_points.txt +20 -0
  34. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/licenses/AUTHORS +7 -0
  35. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/licenses/LICENSE +13 -0
  36. sqlalchemy_jdbcapi-2.0.0.post2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,825 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqlalchemy-jdbcapi
3
+ Version: 2.0.0.post2
4
+ Summary: Modern SQLAlchemy dialect for JDBC connections with native implementation
5
+ Author: Pavel Henrykhsen
6
+ Author-email: Danesh Patel <danesh_patel@outlook.com>
7
+ Maintainer-email: Danesh Patel <danesh_patel@outlook.com>
8
+ License: Apache-2.0
9
+ Project-URL: Homepage, https://github.com/daneshpatel/sqlalchemy-jdbcapi
10
+ Project-URL: Documentation, https://sqlalchemy-jdbcapi.readthedocs.io
11
+ Project-URL: Repository, https://github.com/daneshpatel/sqlalchemy-jdbcapi
12
+ Project-URL: Issues, https://github.com/daneshpatel/sqlalchemy-jdbcapi/issues
13
+ Project-URL: Changelog, https://github.com/daneshpatel/sqlalchemy-jdbcapi/blob/main/CHANGELOG.md
14
+ Keywords: sqlalchemy,jdbc,database,postgresql,oracle,mysql,sql-server,db2,oceanbase
15
+ Classifier: Development Status :: 4 - Beta
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: Apache Software License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: Implementation :: CPython
25
+ Classifier: Topic :: Database
26
+ Classifier: Topic :: Database :: Front-Ends
27
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
28
+ Classifier: Typing :: Typed
29
+ Requires-Python: >=3.10
30
+ Description-Content-Type: text/markdown
31
+ License-File: LICENSE
32
+ License-File: AUTHORS
33
+ Requires-Dist: sqlalchemy>=2.0.0
34
+ Requires-Dist: JPype1>=1.5.0
35
+ Provides-Extra: odbc
36
+ Requires-Dist: pyodbc>=5.0.0; extra == "odbc"
37
+ Provides-Extra: dataframe
38
+ Requires-Dist: pandas>=2.0.0; extra == "dataframe"
39
+ Requires-Dist: polars>=0.20.0; extra == "dataframe"
40
+ Requires-Dist: pyarrow>=14.0.0; extra == "dataframe"
41
+ Provides-Extra: dev
42
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
43
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
44
+ Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
45
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
46
+ Requires-Dist: pytest-xdist>=3.5.0; extra == "dev"
47
+ Requires-Dist: mypy>=1.8.0; extra == "dev"
48
+ Requires-Dist: ruff>=0.2.0; extra == "dev"
49
+ Requires-Dist: pre-commit>=3.6.0; extra == "dev"
50
+ Requires-Dist: tox>=4.11.0; extra == "dev"
51
+ Requires-Dist: build>=1.0.0; extra == "dev"
52
+ Requires-Dist: twine>=5.0.0; extra == "dev"
53
+ Provides-Extra: docs
54
+ Requires-Dist: sphinx>=7.2.0; extra == "docs"
55
+ Requires-Dist: sphinx-rtd-theme>=2.0.0; extra == "docs"
56
+ Requires-Dist: sphinx-autodoc-typehints>=1.25.0; extra == "docs"
57
+ Requires-Dist: myst-parser>=2.0.0; extra == "docs"
58
+ Provides-Extra: types
59
+ Requires-Dist: types-python-dateutil; extra == "types"
60
+ Provides-Extra: all
61
+ Requires-Dist: sqlalchemy-jdbcapi[dataframe,dev,docs,odbc,types]; extra == "all"
62
+ Dynamic: license-file
63
+
64
+ # SQLAlchemy JDBC/ODBC API 2.0
65
+
66
+ [![CI](https://github.com/daneshpatel/sqlalchemy-jdbcapi/workflows/CI/badge.svg)](https://github.com/daneshpatel/sqlalchemy-jdbcapi/actions)
67
+ [![PyPI version](https://img.shields.io/pypi/v/sqlalchemy-jdbcapi.svg)](https://pypi.org/project/sqlalchemy-jdbcapi/)
68
+ [![Python versions](https://img.shields.io/pypi/pyversions/sqlalchemy-jdbcapi.svg)](https://pypi.org/project/sqlalchemy-jdbcapi/)
69
+ [![License](https://img.shields.io/pypi/l/sqlalchemy-jdbcapi.svg)](https://github.com/daneshpatel/sqlalchemy-jdbcapi/blob/main/LICENSE)
70
+ [![Downloads](https://img.shields.io/pypi/dm/sqlalchemy-jdbcapi.svg)](https://pypi.org/project/sqlalchemy-jdbcapi/)
71
+ [![Coverage](https://img.shields.io/badge/coverage-65.47%25-yellow.svg)](htmlcov/index.html)
72
+
73
+ Modern, type-safe SQLAlchemy dialect for JDBC and ODBC connections with native Python implementation.
74
+
75
+ ## ๐Ÿ“š Documentation
76
+
77
+ - **[Quick Start Guide](QUICKSTART.md)** - Get started in 5 minutes
78
+ - **[Usage Guide](USAGE.md)** - Comprehensive usage examples
79
+ - **[Drivers Guide](DRIVERS.md)** - Detailed driver documentation
80
+ - **[Contributing](CONTRIBUTING.md)** - Contribution guidelines
81
+
82
+ ## ๐Ÿš€ Version 2.0 - Major Modernization
83
+
84
+ Version 2.0 is a complete modernization of the library with:
85
+ - โœจ **Automatic JDBC driver download** from Maven Central (zero configuration!)
86
+ - ๐Ÿ”Œ **ODBC support** for native database connectivity
87
+ - ๐ŸŽฏ **Full SQLAlchemy native dialect integration** (ORM, reflection, Alembic, Inspector API)
88
+ - ๐Ÿ“Š **DataFrame integration** (pandas, polars, pyarrow)
89
+ - ๐Ÿ—„๏ธ **12 database dialects** (8 JDBC + 4 ODBC)
90
+ - ๐Ÿ **Modern Python 3.10+** with full type hints
91
+ - โšก **SQLAlchemy 2.0+** compatible
92
+ - ๐Ÿ—๏ธ **SOLID architecture** with clean code principles
93
+
94
+ ## โœจ Features
95
+
96
+ ### JDBC Support
97
+ - **Automatic Driver Download**: JDBC drivers auto-download from Maven Central (zero configuration!)
98
+ - **Native JDBC Bridge**: Our own DB-API 2.0 implementation using JPype (no JayDeBeApi)
99
+ - **Manual Driver Support**: Optional manual driver management via CLASSPATH
100
+ - **8 Major Databases**: PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, DB2, SQLite, OceanBase
101
+
102
+ ### ODBC Support (NEW!)
103
+ - **Native ODBC Connectivity**: Alternative connection method using pyodbc
104
+ - **No JVM Required**: Direct database access without Java runtime
105
+ - **4 Major Databases**: PostgreSQL, MySQL, SQL Server, Oracle
106
+ - **System Integration**: Uses OS-installed ODBC drivers
107
+
108
+ ### SQLAlchemy Integration
109
+ - **Full SQLAlchemy Integration**: Complete native dialect with ORM, reflection, Inspector API, and Alembic support
110
+ - **Database Reflection**: Auto-load tables, columns, constraints, indexes, foreign keys from existing databases
111
+ - **ORM & Automapping**: Full SQLAlchemy ORM support with automatic model generation from existing schemas
112
+ - **DataFrame Integration**: Direct conversion to pandas/polars/arrow for data science workflows
113
+
114
+ ### Code Quality
115
+ - **Type Safe**: Comprehensive type hints throughout
116
+ - **Modern Python**: Python 3.10+ with latest syntax
117
+ - **Best Practices**: Ruff formatting, mypy type checking, comprehensive linting
118
+ - **Clean Architecture**: SOLID principles and design patterns
119
+
120
+ > **๐ŸŽฏ What's Different?** Unlike other JDBC bridges, we provide **true SQLAlchemy native dialect integration**. This means you can use all SQLAlchemy features including table autoload, Inspector API, Alembic migrations, and ORM automapping - not just basic SQL execution!
121
+
122
+ ## ๐Ÿ“ฆ Installation
123
+
124
+ ```bash
125
+ # Basic installation
126
+ pip install sqlalchemy-jdbcapi
127
+
128
+ # With DataFrame support (pandas, polars, pyarrow)
129
+ pip install sqlalchemy-jdbcapi[dataframe]
130
+
131
+ # For development
132
+ pip install sqlalchemy-jdbcapi[dev]
133
+ ```
134
+
135
+ ## ๐Ÿ—„๏ธ Supported Databases
136
+
137
+ ### JDBC Dialects (Auto-Download Supported)
138
+
139
+ | Database | Connection URL | Auto-Download |
140
+ |----------|----------------|---------------|
141
+ | PostgreSQL | `jdbcapi+postgresql://user:pass@host:5432/db` | โœ… |
142
+ | Oracle | `jdbcapi+oracle://user:pass@host:1521/SID` | โœ… |
143
+ | MySQL | `jdbcapi+mysql://user:pass@host:3306/db` | โœ… |
144
+ | MariaDB | `jdbcapi+mariadb://user:pass@host:3306/db` | โœ… |
145
+ | SQL Server | `jdbcapi+mssql://user:pass@host:1433/db` | โœ… |
146
+ | DB2 | `jdbcapi+db2://user:pass@host:50000/db` | โœ… |
147
+ | OceanBase | `jdbcapi+oceanbase://user:pass@host:2881/db` | โœ… |
148
+ | SQLite | `jdbcapi+sqlite:///path/to/db.db` | โœ… |
149
+
150
+ ### ODBC Dialects (OS-Installed Drivers Required)
151
+
152
+ | Database | Connection URL | Install Guide |
153
+ |----------|----------------|---------------|
154
+ | PostgreSQL | `odbcapi+postgresql://user:pass@host:5432/db` | [See DRIVERS.md](DRIVERS.md#postgresql-odbc) |
155
+ | MySQL | `odbcapi+mysql://user:pass@host:3306/db` | [See DRIVERS.md](DRIVERS.md#mysql-odbc) |
156
+ | SQL Server | `odbcapi+mssql://user:pass@host:1433/db` | [See DRIVERS.md](DRIVERS.md#microsoft-sql-server-odbc) |
157
+ | Oracle | `odbcapi+oracle://user:pass@host:1521/service` | [See DRIVERS.md](DRIVERS.md#oracle-odbc) |
158
+
159
+ For detailed driver documentation, see **[DRIVERS.md](DRIVERS.md)**.
160
+
161
+ ## ๐Ÿš€ Quick Start
162
+
163
+ ### JDBC with Auto-Download (Recommended!)
164
+
165
+ No setup required - drivers auto-download on first use!
166
+
167
+ ```python
168
+ from sqlalchemy import create_engine, text
169
+
170
+ # PostgreSQL - driver auto-downloads from Maven Central
171
+ engine = create_engine('jdbcapi+postgresql://user:password@localhost:5432/mydb')
172
+
173
+ # Execute queries
174
+ with engine.connect() as conn:
175
+ result = conn.execute(text("SELECT version()"))
176
+ print(result.scalar())
177
+ ```
178
+
179
+ Drivers are cached in `~/.sqlalchemy-jdbcapi/drivers/` for future use.
180
+
181
+ ### ODBC (Alternative)
182
+
183
+ Requires ODBC driver installation (see [DRIVERS.md](DRIVERS.md)):
184
+
185
+ ```python
186
+ from sqlalchemy import create_engine
187
+
188
+ # PostgreSQL via ODBC (no JVM needed!)
189
+ engine = create_engine('odbcapi+postgresql://user:password@localhost:5432/mydb')
190
+
191
+ with engine.connect() as conn:
192
+ result = conn.execute(text("SELECT version()"))
193
+ print(result.scalar())
194
+ ```
195
+
196
+ ### Manual JDBC Driver Setup (Optional)
197
+
198
+ If you prefer to manage drivers manually:
199
+
200
+ ```bash
201
+ # Set CLASSPATH environment variable
202
+ export CLASSPATH="/path/to/postgresql-42.7.1.jar"
203
+ ```
204
+
205
+ ```python
206
+ from sqlalchemy import create_engine
207
+
208
+ # Will use driver from CLASSPATH
209
+ engine = create_engine('jdbcapi+postgresql://user:password@localhost/mydb')
210
+ ```
211
+
212
+ For detailed usage, see **[QUICKSTART.md](QUICKSTART.md)** and **[USAGE.md](USAGE.md)**.
213
+
214
+ ## ๐Ÿ’ก Examples
215
+
216
+ **๐Ÿ“ Full Examples Available:**
217
+ - **[examples/basic_usage.py](examples/basic_usage.py)** - 8 fundamental usage patterns (JDBC, ODBC, ORM, pooling, transactions)
218
+ - **[examples/data_analysis.py](examples/data_analysis.py)** - 8 data science examples (pandas, polars, ETL, analytics, streaming)
219
+ - **[examples/README.md](examples/README.md)** - Complete guide with configuration and troubleshooting
220
+
221
+ ### PostgreSQL
222
+
223
+ ```python
224
+ from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData
225
+
226
+ # Create engine
227
+ engine = create_engine('jdbcapi+postgresql://user:pass@localhost:5432/mydb')
228
+
229
+ # Define schema
230
+ metadata = MetaData()
231
+ users = Table('users', metadata,
232
+ Column('id', Integer, primary_key=True),
233
+ Column('name', String(50)),
234
+ Column('email', String(100))
235
+ )
236
+
237
+ # Create tables
238
+ metadata.create_all(engine)
239
+
240
+ # Insert data
241
+ with engine.connect() as conn:
242
+ conn.execute(users.insert().values(name='Alice', email='alice@example.com'))
243
+ conn.commit()
244
+
245
+ # Query data
246
+ with engine.connect() as conn:
247
+ result = conn.execute(users.select())
248
+ for row in result:
249
+ print(f"User: {row.name}, Email: {row.email}")
250
+ ```
251
+
252
+ ### Oracle
253
+
254
+ ```python
255
+ from sqlalchemy import create_engine, text
256
+
257
+ # Standard connection
258
+ engine = create_engine('jdbcapi+oracle://user:password@localhost:1521/ORCL')
259
+
260
+ # TNS name connection
261
+ engine = create_engine('jdbcapi+oracle://user:password@TNSNAME')
262
+
263
+ # With query parameters
264
+ from sqlalchemy.engine.url import URL
265
+
266
+ url = URL.create(
267
+ 'jdbcapi+oracle',
268
+ username='user',
269
+ password='password',
270
+ host='localhost',
271
+ port=1521,
272
+ database='ORCL',
273
+ query={'ssl': 'true'}
274
+ )
275
+ engine = create_engine(url)
276
+ ```
277
+
278
+ ### MySQL / MariaDB
279
+
280
+ ```python
281
+ from sqlalchemy import create_engine
282
+
283
+ # MySQL
284
+ engine = create_engine('jdbcapi+mysql://root:password@localhost:3306/mydb')
285
+
286
+ # MariaDB
287
+ engine = create_engine('jdbcapi+mariadb://root:password@localhost:3306/mydb')
288
+
289
+ # With SSL
290
+ engine = create_engine(
291
+ 'jdbcapi+mysql://user:pass@localhost:3306/mydb?useSSL=true&requireSSL=true'
292
+ )
293
+ ```
294
+
295
+ ### SQL Server
296
+
297
+ ```python
298
+ from sqlalchemy import create_engine
299
+
300
+ # Standard connection
301
+ engine = create_engine('jdbcapi+mssql://user:password@localhost:1433/mydb')
302
+
303
+ # Windows Authentication (if supported by JDBC driver)
304
+ engine = create_engine(
305
+ 'jdbcapi+mssql://localhost:1433/mydb?integratedSecurity=true'
306
+ )
307
+ ```
308
+
309
+ ### DB2
310
+
311
+ ```python
312
+ from sqlalchemy import create_engine
313
+
314
+ # DB2 LUW
315
+ engine = create_engine('jdbcapi+db2://user:password@localhost:50000/mydb')
316
+
317
+ # DB2 z/OS (mainframe)
318
+ engine = create_engine('jdbcapi+db2://user:password@mainframe:446/DBCG')
319
+ ```
320
+
321
+ ### OceanBase
322
+
323
+ ```python
324
+ from sqlalchemy import create_engine
325
+ from urllib.parse import quote
326
+
327
+ # OceanBase with tenant and cluster
328
+ user = quote('username@tenant#cluster')
329
+ engine = create_engine(f'jdbcapi+oceanbase://{user}:password@localhost:2881/mydb')
330
+ ```
331
+
332
+ ## ๐ŸŽฏ Full SQLAlchemy Integration
333
+
334
+ Version 2.0 provides **complete SQLAlchemy native dialect integration** with full ORM, reflection, and Inspector API support. See [SQLALCHEMY_INTEGRATION.md](SQLALCHEMY_INTEGRATION.md) for comprehensive documentation.
335
+
336
+ ### ORM Support
337
+
338
+ Use declarative models with full relationship support:
339
+
340
+ ```python
341
+ from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
342
+ from sqlalchemy.orm import declarative_base, relationship, Session
343
+
344
+ Base = declarative_base()
345
+
346
+ class User(Base):
347
+ __tablename__ = 'users'
348
+
349
+ id = Column(Integer, primary_key=True)
350
+ name = Column(String(50))
351
+ email = Column(String(100))
352
+ posts = relationship("Post", back_populates="author")
353
+
354
+ class Post(Base):
355
+ __tablename__ = 'posts'
356
+
357
+ id = Column(Integer, primary_key=True)
358
+ title = Column(String(200))
359
+ user_id = Column(Integer, ForeignKey('users.id'))
360
+ author = relationship("User", back_populates="posts")
361
+
362
+ # Create engine
363
+ engine = create_engine('jdbcapi+postgresql://user:pass@localhost:5432/mydb')
364
+
365
+ # Create tables
366
+ Base.metadata.create_all(engine)
367
+
368
+ # Use ORM
369
+ session = Session(engine)
370
+ user = User(name='Alice', email='alice@example.com')
371
+ user.posts.append(Post(title='My First Post'))
372
+ session.add(user)
373
+ session.commit()
374
+
375
+ # Query with relationships
376
+ users = session.query(User).join(User.posts).filter(Post.title.like('%First%')).all()
377
+ ```
378
+
379
+ ### Table Reflection & Auto-load
380
+
381
+ Automatically load existing table structures from your database:
382
+
383
+ ```python
384
+ from sqlalchemy import Table, MetaData, select
385
+
386
+ metadata = MetaData()
387
+
388
+ # Auto-load table structure from database
389
+ users = Table('users', metadata, autoload_with=engine)
390
+
391
+ # Now you can use it!
392
+ print(users.columns.keys()) # ['id', 'name', 'email']
393
+ print(users.primary_key) # PrimaryKeyConstraint('id')
394
+
395
+ # Query the reflected table
396
+ with engine.connect() as conn:
397
+ stmt = select(users).where(users.c.name == 'Alice')
398
+ result = conn.execute(stmt)
399
+ for row in result:
400
+ print(row)
401
+ ```
402
+
403
+ ### Database Inspector
404
+
405
+ Explore your database schema programmatically:
406
+
407
+ ```python
408
+ from sqlalchemy import inspect
409
+
410
+ inspector = inspect(engine)
411
+
412
+ # List all schemas
413
+ schemas = inspector.get_schema_names()
414
+ print(f"Schemas: {schemas}")
415
+
416
+ # List all tables
417
+ tables = inspector.get_table_names(schema='public')
418
+ print(f"Tables: {tables}")
419
+
420
+ # Get column information
421
+ columns = inspector.get_columns('users', schema='public')
422
+ for col in columns:
423
+ print(f"{col['name']}: {col['type']} (nullable={col['nullable']})")
424
+
425
+ # Get primary keys
426
+ pk = inspector.get_pk_constraint('users', schema='public')
427
+ print(f"Primary key: {pk['constrained_columns']}")
428
+
429
+ # Get foreign keys
430
+ fks = inspector.get_foreign_keys('posts', schema='public')
431
+ for fk in fks:
432
+ print(f"{fk['constrained_columns']} -> {fk['referred_table']}.{fk['referred_columns']}")
433
+
434
+ # Get indexes
435
+ indexes = inspector.get_indexes('users', schema='public')
436
+ for idx in indexes:
437
+ print(f"Index {idx['name']}: {idx['column_names']} (unique={idx['unique']})")
438
+ ```
439
+
440
+ ### ORM Automapping
441
+
442
+ Automatically generate ORM models from existing databases:
443
+
444
+ ```python
445
+ from sqlalchemy.ext.automap import automap_base
446
+ from sqlalchemy.orm import Session
447
+
448
+ # Reflect entire database schema
449
+ Base = automap_base()
450
+ Base.prepare(engine, reflect=True)
451
+
452
+ # Classes are generated automatically!
453
+ User = Base.classes.users
454
+ Post = Base.classes.posts
455
+
456
+ # Use them like regular ORM models
457
+ session = Session(engine)
458
+ users = session.query(User).all()
459
+ for user in users:
460
+ print(f"{user.name}: {len(user.posts)} posts")
461
+ ```
462
+
463
+ ### Alembic Migrations
464
+
465
+ Full support for Alembic database migrations:
466
+
467
+ ```bash
468
+ # Initialize Alembic
469
+ alembic init migrations
470
+
471
+ # Configure alembic.ini
472
+ sqlalchemy.url = jdbcapi+postgresql://user:pass@localhost:5432/mydb
473
+
474
+ # Auto-generate migration from model changes
475
+ alembic revision --autogenerate -m "Add user and post tables"
476
+
477
+ # Apply migrations
478
+ alembic upgrade head
479
+
480
+ # Rollback
481
+ alembic downgrade -1
482
+ ```
483
+
484
+ ### Reflect and Migrate Between Databases
485
+
486
+ Copy schemas between different database systems:
487
+
488
+ ```python
489
+ from sqlalchemy import MetaData, create_engine
490
+
491
+ # Reflect schema from PostgreSQL
492
+ pg_engine = create_engine('jdbcapi+postgresql://user:pass@localhost/source_db')
493
+ metadata = MetaData()
494
+ metadata.reflect(bind=pg_engine)
495
+
496
+ # Migrate to Oracle
497
+ oracle_engine = create_engine('jdbcapi+oracle://user:pass@localhost:1521/target_db')
498
+ metadata.create_all(oracle_engine)
499
+
500
+ # Copy data
501
+ for table in metadata.sorted_tables:
502
+ with pg_engine.connect() as source:
503
+ data = source.execute(table.select()).fetchall()
504
+ if data:
505
+ with oracle_engine.connect() as target:
506
+ target.execute(table.insert(), [dict(row._mapping) for row in data])
507
+ target.commit()
508
+ ```
509
+
510
+ ## ๐Ÿ“Š DataFrame Integration
511
+
512
+ Version 2.0 adds powerful DataFrame integration for data science workflows:
513
+
514
+ ```python
515
+ from sqlalchemy import create_engine
516
+
517
+ engine = create_engine('jdbcapi+postgresql://user:pass@localhost/mydb')
518
+
519
+ # Method 1: Using helper functions
520
+ from sqlalchemy_jdbcapi.jdbc.dataframe import cursor_to_pandas
521
+
522
+ with engine.connect() as conn:
523
+ cursor = conn.connection.cursor()
524
+ cursor.execute("SELECT * FROM large_table WHERE date > '2024-01-01'")
525
+
526
+ # Convert to pandas
527
+ df = cursor_to_pandas(cursor)
528
+ print(df.describe())
529
+
530
+ # Or polars
531
+ from sqlalchemy_jdbcapi.jdbc.dataframe import cursor_to_polars
532
+ df = cursor_to_polars(cursor)
533
+ print(df.head())
534
+
535
+ # Or Apache Arrow
536
+ from sqlalchemy_jdbcapi.jdbc.dataframe import cursor_to_arrow
537
+ table = cursor_to_arrow(cursor)
538
+ print(table.schema)
539
+ ```
540
+
541
+ ```python
542
+ # Method 2: Using cursor convenience methods
543
+ with engine.connect() as conn:
544
+ cursor = conn.connection.cursor()
545
+ cursor.execute("SELECT * FROM users")
546
+
547
+ # Direct conversion
548
+ df = cursor.to_pandas() # pandas DataFrame
549
+ df = cursor.to_polars() # polars DataFrame
550
+ table = cursor.to_arrow() # Arrow Table
551
+ dicts = cursor.to_dict() # List of dictionaries
552
+ ```
553
+
554
+ ## ๐ŸŽฏ Advanced Usage
555
+
556
+ ### Connection Pooling
557
+
558
+ ```python
559
+ from sqlalchemy import create_engine
560
+ from sqlalchemy.pool import QueuePool
561
+
562
+ engine = create_engine(
563
+ 'jdbcapi+postgresql://user:pass@localhost/mydb',
564
+ poolclass=QueuePool,
565
+ pool_size=5,
566
+ max_overflow=10,
567
+ pool_timeout=30,
568
+ pool_recycle=3600
569
+ )
570
+ ```
571
+
572
+ ### Context Managers
573
+
574
+ ```python
575
+ from sqlalchemy import create_engine
576
+
577
+ engine = create_engine('jdbcapi+postgresql://user:pass@localhost/mydb')
578
+
579
+ # Connection context
580
+ with engine.connect() as conn:
581
+ result = conn.execute(text("SELECT * FROM users"))
582
+ # Connection automatically closed
583
+
584
+ # Transaction context
585
+ with engine.begin() as conn:
586
+ conn.execute(text("INSERT INTO users (name) VALUES (:name)"), {"name": "Bob"})
587
+ # Automatically committed (or rolled back on exception)
588
+ ```
589
+
590
+ ### Batch Operations
591
+
592
+ ```python
593
+ from sqlalchemy import create_engine, text
594
+
595
+ engine = create_engine('jdbcapi+postgresql://user:pass@localhost/mydb')
596
+
597
+ # Batch insert
598
+ data = [
599
+ {"name": "Alice", "age": 30},
600
+ {"name": "Bob", "age": 25},
601
+ {"name": "Charlie", "age": 35}
602
+ ]
603
+
604
+ with engine.begin() as conn:
605
+ conn.execute(
606
+ text("INSERT INTO users (name, age) VALUES (:name, :age)"),
607
+ data
608
+ )
609
+ ```
610
+
611
+ ### Type Hints Support
612
+
613
+ ```python
614
+ from sqlalchemy import create_engine, text, Engine, Connection, Result
615
+ from sqlalchemy.engine import Row
616
+
617
+ engine: Engine = create_engine('jdbcapi+postgresql://user:pass@localhost/mydb')
618
+
619
+ def get_users(conn: Connection) -> list[Row]:
620
+ result: Result = conn.execute(text("SELECT * FROM users"))
621
+ return list(result)
622
+
623
+ with engine.connect() as conn:
624
+ users = get_users(conn)
625
+ ```
626
+
627
+ ## ๐Ÿ—๏ธ Architecture
628
+
629
+ ### Project Structure
630
+
631
+ ```
632
+ src/sqlalchemy_jdbcapi/
633
+ โ”œโ”€โ”€ __init__.py # Main package
634
+ โ”œโ”€โ”€ jdbc/ # JDBC bridge layer (DB-API 2.0)
635
+ โ”‚ โ”œโ”€โ”€ connection.py # Connection class
636
+ โ”‚ โ”œโ”€โ”€ cursor.py # Cursor class
637
+ โ”‚ โ”œโ”€โ”€ exceptions.py # Exception hierarchy
638
+ โ”‚ โ”œโ”€โ”€ types.py # DB-API type objects
639
+ โ”‚ โ”œโ”€โ”€ type_converter.py # JDBC โ†” Python type conversion
640
+ โ”‚ โ”œโ”€โ”€ jvm.py # JVM management
641
+ โ”‚ โ””โ”€โ”€ dataframe.py # DataFrame integration
642
+ โ””โ”€โ”€ dialects/ # SQLAlchemy dialects
643
+ โ”œโ”€โ”€ base.py # Base dialect (Template Method)
644
+ โ”œโ”€โ”€ postgresql.py # PostgreSQL dialect
645
+ โ”œโ”€โ”€ oracle.py # Oracle dialect
646
+ โ”œโ”€โ”€ mysql.py # MySQL/MariaDB dialects
647
+ โ”œโ”€โ”€ mssql.py # SQL Server dialect
648
+ โ”œโ”€โ”€ db2.py # DB2 dialect
649
+ โ”œโ”€โ”€ oceanbase.py # OceanBase dialect
650
+ โ””โ”€โ”€ sqlite.py # SQLite dialect
651
+ ```
652
+
653
+ ### Design Patterns Used
654
+
655
+ - **Template Method**: `BaseJDBCDialect` provides common functionality
656
+ - **Strategy**: Type conversion strategies for different SQL types
657
+ - **Factory**: Dialect creation and registration
658
+ - **Adapter**: SQLAlchemy URL to JDBC connection arguments
659
+ - **Dependency Injection**: Driver configuration
660
+
661
+ ### SQLAlchemy Reflection Implementation
662
+
663
+ All reflection methods are implemented in `BaseJDBCDialect` using JDBC's `DatabaseMetaData` API:
664
+
665
+ - **`get_table_names()`** - Uses `DatabaseMetaData.getTables()`
666
+ - **`get_columns()`** - Uses `DatabaseMetaData.getColumns()`
667
+ - **`get_pk_constraint()`** - Uses `DatabaseMetaData.getPrimaryKeys()`
668
+ - **`get_foreign_keys()`** - Uses `DatabaseMetaData.getImportedKeys()`
669
+ - **`get_indexes()`** - Uses `DatabaseMetaData.getIndexInfo()`
670
+ - **`has_table()`** - Uses `DatabaseMetaData.getTables()`
671
+ - **`get_schema_names()`** - Uses `DatabaseMetaData.getSchemas()`
672
+ - **`get_view_names()`** - Uses `DatabaseMetaData.getTables()` with VIEW type
673
+ - **`get_unique_constraints()`** - Extracted from index information
674
+ - **`get_check_constraints()`** - Database-specific implementations
675
+
676
+ These methods enable full SQLAlchemy features:
677
+ - โœ… Table autoload (`Table(..., autoload_with=engine)`)
678
+ - โœ… Inspector API (`inspect(engine).get_table_names()`)
679
+ - โœ… Alembic migrations (`alembic revision --autogenerate`)
680
+ - โœ… ORM automapping (`Base.prepare(engine, reflect=True)`)
681
+ - โœ… Cross-database schema migration
682
+
683
+ ## ๐Ÿ”ง Development
684
+
685
+ ### Setup Development Environment
686
+
687
+ ```bash
688
+ git clone https://github.com/daneshpatel/sqlalchemy-jdbcapi.git
689
+ cd sqlalchemy-jdbcapi
690
+
691
+ # Create virtual environment
692
+ python -m venv .venv
693
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
694
+
695
+ # Install in development mode
696
+ pip install -e ".[dev]"
697
+
698
+ # Install pre-commit hooks
699
+ pre-commit install
700
+ ```
701
+
702
+ ### Running Tests
703
+
704
+ **Test Suite Statistics:**
705
+ - ๐Ÿ“Š **168 total tests** (149 unit/integration, 19 functional)
706
+ - โœ… **100% passing** (2 skipped - optional pyodbc)
707
+ - ๐Ÿ“ˆ **46.77% coverage** (90%+ on JDBC/ODBC core components)
708
+ - ๐Ÿงช **3 test categories**: Unit, Integration, Functional
709
+
710
+ ```bash
711
+ # Run all tests (unit + integration)
712
+ pytest
713
+
714
+ # Run with coverage report
715
+ pytest --cov=sqlalchemy_jdbcapi --cov-report=html
716
+
717
+ # Run functional tests (requires real databases)
718
+ pytest tests/functional/ -v -m functional
719
+
720
+ # Run network tests (requires internet for Maven Central)
721
+ pytest tests/functional/ -v -m network
722
+
723
+ # Run specific test file
724
+ pytest tests/unit/test_dialects.py
725
+
726
+ # Run with markers
727
+ pytest -m "not slow" # Skip slow tests
728
+ pytest -m "not functional" # Skip functional tests (default)
729
+
730
+ # View coverage report
731
+ open htmlcov/index.html # macOS
732
+ xdg-open htmlcov/index.html # Linux
733
+ ```
734
+
735
+ ### Code Quality
736
+
737
+ ```bash
738
+ # Format code
739
+ ruff format src tests
740
+
741
+ # Lint code
742
+ ruff check src tests
743
+
744
+ # Type check
745
+ mypy src
746
+
747
+ # Run all pre-commit hooks
748
+ pre-commit run --all-files
749
+ ```
750
+
751
+ ## ๐Ÿ“ Migration from 1.x to 2.0
752
+
753
+ ### Key Changes
754
+
755
+ 1. **Python Version**: Minimum is now Python 3.10
756
+ 2. **Dependencies**: JayDeBeApi โ†’ JPype1 (automatic)
757
+ 3. **SQLAlchemy**: Now requires SQLAlchemy 2.0+
758
+
759
+ ### Migration Steps
760
+
761
+ ```python
762
+ # 1.x code (still works in 2.0!)
763
+ from sqlalchemy import create_engine
764
+
765
+ engine = create_engine('jdbcapi+pgjdbc://user:pass@localhost/db')
766
+ # โœ… Still works with backward compatibility
767
+
768
+ # 2.0 recommended style
769
+ engine = create_engine('jdbcapi+postgresql://user:pass@localhost/db')
770
+ # โœจ New preferred naming
771
+
772
+ # New features in 2.0
773
+ with engine.connect() as conn:
774
+ cursor = conn.connection.cursor()
775
+ cursor.execute("SELECT * FROM large_dataset")
776
+
777
+ # DataFrame integration (new in 2.0)
778
+ df = cursor.to_pandas()
779
+ print(df.head())
780
+ ```
781
+
782
+ See [CHANGELOG.md](CHANGELOG.md) for complete migration guide.
783
+
784
+ ## ๐Ÿค Contributing
785
+
786
+ We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
787
+
788
+ ### Quick Contribution Guide
789
+
790
+ 1. Fork the repository
791
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
792
+ 3. Make your changes
793
+ 4. Run tests and linting (`pytest && ruff check`)
794
+ 5. Commit your changes (`git commit -m 'Add amazing feature'`)
795
+ 6. Push to the branch (`git push origin feature/amazing-feature`)
796
+ 7. Open a Pull Request
797
+
798
+ ## ๐Ÿ“„ License
799
+
800
+ This project is licensed under the Apache License 2.0 - see [LICENSE](LICENSE) file for details.
801
+
802
+ ## ๐Ÿ™ Acknowledgments
803
+
804
+ - Original library by Danesh Patel and Pavel Henrykhsen
805
+ - SQLAlchemy team for the excellent ORM framework
806
+ - JPype contributors for the Java-Python bridge
807
+ - All contributors who have helped improve this library
808
+
809
+ ## ๐Ÿ“š Links
810
+
811
+ - **Documentation**: https://sqlalchemy-jdbcapi.readthedocs.io (coming soon)
812
+ - **SQLAlchemy Integration Guide**: [SQLALCHEMY_INTEGRATION.md](SQLALCHEMY_INTEGRATION.md)
813
+ - **PyPI**: https://pypi.org/project/sqlalchemy-jdbcapi/
814
+ - **GitHub**: https://github.com/daneshpatel/sqlalchemy-jdbcapi
815
+ - **Issues**: https://github.com/daneshpatel/sqlalchemy-jdbcapi/issues
816
+ - **Changelog**: [CHANGELOG.md](CHANGELOG.md)
817
+
818
+ ## ๐Ÿ’ฌ Support
819
+
820
+ - **Issues**: Report bugs or request features on [GitHub Issues](https://github.com/daneshpatel/sqlalchemy-jdbcapi/issues)
821
+ - **Discussions**: Ask questions on [GitHub Discussions](https://github.com/daneshpatel/sqlalchemy-jdbcapi/discussions)
822
+
823
+ ---
824
+
825
+ Made with โค๏ธ by the SQLAlchemy JDBC API team