REST-Jdbc — Microsoft Graph

Die Microsoft Graph API liefert Zugriff auf Microsoft-365-Daten (Benutzer, Gruppen, E-Mail, Kalender usw.). Dieses Beispiel zeigt OAuth 2.0 Client Credentials für mandantenweite Leseabfragen und OAuth 2.0 Device Code für delegierte Abfragen und Schreibvorgänge (/me/…).

Schnellstart mit dem JDBC-Client

Benutzer und Gruppen (OAuth2 Client Credentials)

java -jar restjdbc.jar graph-oauth2.sql
  1. Azure-App mit Anwendungsberechtigungen anlegen (siehe Abschnitt 4)
  2. In graph-oauth2.sql tenant, clientId und clientSecret ersetzen
  3. Befehl im Ordner mit graph-spec.json ausführen
connect 'jdbc:rest:https://graph.microsoft.com/v1.0|spec=graph-spec.json,auth=oauth2,tenant=…,clientId=…,clientSecret=…,scope=https://graph.microsoft.com/.default'

Posteingang (OAuth2 Device Code, delegiert)

Der Endpunkt /me/messages erfordert ein delegiertes Token (angemeldeter Benutzer). Dafür eignet sich der Device Code Flow im Treiber:

java -jar restjdbc.jar graph-devicecode.sql
  1. Azure-App als Public Client konfigurieren, delegierte Berechtigung Mail.Read anlegen
  2. In graph-devicecode.sql tenant und clientId ersetzen
  3. Beim Verbindungsaufbau erscheint URL/Code zur Anmeldung im Browser
connect 'jdbc:rest:https://graph.microsoft.com/v1.0|spec=graph-spec.json,auth=oauth2,oauth2GrantType=device_code,tenant=…,clientId=…,scope=Mail.Read offline_access'

Alternativ weiterhin manuelles Bearer-Token: graph-bearer.sql

Kalender (OAuth2 Device Code, delegiert, Schreiben)

Termine des angemeldeten Benutzers lesen und schreiben (INSERT, UPDATE, DELETE):

java -jar restjdbc.jar graph-calendar.sql
  1. Delegierte Berechtigung Calendars.ReadWrite in der Azure-App anlegen
  2. In graph-calendar.sql tenant und clientId ersetzen
  3. Scope: Calendars.ReadWrite offline_access User.Read
connect 'jdbc:rest:https://graph.microsoft.com/v1.0|spec=graph-spec.json,auth=oauth2,oauth2GrantType=device_code,tenant=…,clientId=…,scope=Calendars.ReadWrite offline_access User.Read'

Verschachtelte Graph-Felder (start, end, body, location) werden über jsonPath und defaultWrite in der Spec abgebildet — siehe Spalten in graph-spec.json, Entity calendar_events.

Benutzer-Kalender (OAuth2 Client Credentials, App-Berechtigung)

Termine eines beliebigen Benutzers im Mandanten lesen und schreiben (/users/{userId}/events):

java -jar restjdbc.jar graph-user-calendar.sql
  1. Anwendungsberechtigung Calendars.ReadWrite anlegen und Admin-Zustimmung erteilen
  2. In graph-user-calendar.sql tenant, clientId, clientSecret und USER-OBJECT-ID ersetzen
  3. Scope: https://graph.microsoft.com/.default

Der Pfad-Parameter userId wird per Spalte bzw. FILTER userId='…' gesetzt (Platzhalter {userId} in der Spec). Beispiel:

INSERT INTO user_calendar_events ("userId", subject, "start_dateTime", "end_dateTime")
VALUES ('USER-OBJECT-ID', 'Meeting', '2026-07-01T10:00:00', '2026-07-01T11:00:00');

UPDATE user_calendar_events SET subject = 'Meeting (neu)' FILTER userId='USER-OBJECT-ID' AND id=EVENT-ID;

1. JDBC-URL

jdbc:rest:https://graph.microsoft.com/v1.0

Die Basis-URL der API steht in der JDBC-URL.

Bestandteil Wert
Treiber-Präfix jdbc:rest:
API-Basis-URL https://graph.microsoft.com/v1.0
Schema (SQL) public (Standard)

Beispiel-Verbindung (Java, OAuth2):

Properties props = new Properties();
props.setProperty("spec", "/pfad/zu/graph-spec.json");
props.setProperty("auth", "oauth2");
props.setProperty("tenant", System.getenv("AZURE_TENANT_ID"));
props.setProperty("clientId", System.getenv("AZURE_CLIENT_ID"));
props.setProperty("clientSecret", System.getenv("AZURE_CLIENT_SECRET"));
props.setProperty("scope", "https://graph.microsoft.com/.default");

Connection conn = DriverManager.getConnection(
    "jdbc:rest:https://graph.microsoft.com/v1.0", props);

2. Connection Properties

OAuth2 Client Credentials (users, groups)

Property Wert Erforderlich
spec Pfad zur Spec Ja
auth oauth2 Ja
tenant Azure AD Tenant-ID Ja (oder tokenUrl)
clientId App-Registrierung (Client-ID) Ja
oauth2GrantType client_credentials (Standard) Nein
clientSecret Client Secret Ja
scope https://graph.microsoft.com/.default Ja

Alternativ statt tenant:

props.setProperty("tokenUrl",
    "https://login.microsoftonline.com/Ihre-Tenant-ID/oauth2/v2.0/token");

Der Treiber holt und erneuert das Access Token automatisch.

OAuth2 Device Code (messages /me)

Property Wert Erforderlich
auth oauth2 Ja
oauth2GrantType device_code Ja
tenant Azure AD Tenant-ID Ja
clientId App-Registrierung (Public Client) Ja
scope Mail.Read offline_access Ja
clientSecret Nein (Public Client)

Der Treiber zeigt die Anmelde-URL, wartet auf die Benutzer-Anmeldung und erneuert das Token per Refresh Token.

Bearer (Alternative für messages /me)

Property Wert Erforderlich
auth bearer Ja
token Delegiertes Access Token Ja

Spec-Datei: graph-spec.json

3. Spec-Datei

Datei: graph-spec.json

Tabellen

SQL-Tabelle REST-Pfad Auth-Typ Anmerkung
users /users OAuth2 Client Credentials User.Read.All
groups /groups OAuth2 Client Credentials Group.Read.All
messages /me/messages OAuth2 Device Code oder Bearer Mail.Read delegiert
calendar_events /me/events OAuth2 Device Code oder Bearer Calendars.ReadWrite delegiert, INSERT/UPDATE/DELETE
user_calendar_events /users/{userId}/events OAuth2 Client Credentials Calendars.ReadWrite App, Pfad-Parameter userId

Gemeinsame Spec-Einstellungen für OData:

"dataPath": "/value",
"filterParam": "$filter",
"selectParam": "$select",
"orderByParam": "$orderby",
"pagination": {
  "type": "nextLink",
  "limitParam": "$top",
  "nextLinkPath": "/@odata.nextLink",
  "defaultLimit": 100
}
  • nextLink: der Treiber folgt @odata.nextLink automatisch über alle Seiten
  • filterParam: FILTER-Inhalt wird als OData-$filter gesendet (native Syntax)
  • selectParam: nur im SELECT genannte Spalten werden als $select angefordert
  • jsonPath / selectName: verschachtelte Felder (z. B. Absender bei messages)

"write": false — Schreiben ist in diesem Beispiel deaktiviert.

4. Azure App-Registrierung

Für users / groups (Client Credentials)

  1. Microsoft Entra IDApp-RegistrierungenNeue Registrierung
  2. Zertifikate & Geheimnisse → Client Secret anlegen
  3. API-BerechtigungenMicrosoft GraphAnwendungsberechtigungen:
    • User.Read.All
    • Group.Read.All
  4. Administratorzustimmung erteilen

Für Benutzer-Kalender (user_calendar_events) zusätzlich Anwendungsberechtigung: Calendars.ReadWrite.

Für messages / calendar_events (delegiert)

Zusätzlich Delegierte Berechtigungen: Mail.Read (Posteingang), Calendars.ReadWrite (Kalender). Public Client Flows für Device Code aktivieren, oder Token manuell für Bearer-Auth verwenden.

5. SQL-Beispiele

Spaltennamen aus der Spec mit gemischter Schreibweise (camelCase, z. B. displayName, start_dateTime) müssen in SELECT, INSERT und UPDATE in doppelten Anführungszeichen stehen — der SQL-Parser normalisiert unquoted Identifier sonst in Kleinbuchstaben. In FILTER und ORDERBY gilt das nicht: dort OData-Syntax mit Graph-Eigenschaftsnamen (displayName, accountEnabled).

Benutzer (OAuth2)

SELECT id, "displayName", mail, "userPrincipalName" FROM users;

SELECT id, "displayName", mail FROM users
  FILTER startswith(displayName,'M') AND accountEnabled eq true
  ORDERBY displayName asc;
SQL HTTP (vereinfacht)
SELECT id, "displayName", mail FROM users GET /users?$top=100&$select=id,displayName,mail
… FILTER startswith(…) …&$filter=startswith(displayName,'M') AND accountEnabled eq true

FILTER enthält OData-Ausdruck — nicht param=wert (dafür ist filterParam in der Spec gesetzt).

Gruppen (OAuth2)

SELECT id, "displayName", "mailEnabled" FROM groups
  FILTER mailEnabled eq true AND securityEnabled eq true;

Posteingang (Device Code oder Bearer)

SELECT id, subject, from_address, "receivedDateTime" FROM messages
  FILTER isRead eq false;

Mit graph-devicecode.sql (OAuth2 Device Code) oder graph-bearer.sql (fertiges Token).

Die Spalten from_address und from_name nutzen jsonPath; $select fordert das übergeordnete Feld from an (selectName).

Kalender (Device Code oder Bearer)

INSERT INTO calendar_events (subject, "start_dateTime", "end_dateTime", body_content)
VALUES ('Team-Meeting', '2026-07-01T10:00:00', '2026-07-01T11:00:00', 'Agenda …');

SELECT id, subject, "start_dateTime", "end_dateTime" FROM calendar_events
  FILTER startswith(subject,'Team-Meeting');

UPDATE calendar_events SET subject = 'Team-Meeting (verschoben)' FILTER id=EVENT-ID;

DELETE FROM calendar_events FILTER id=EVENT-ID;
SQL HTTP (vereinfacht)
INSERT … POST /me/events mit JSON {subject, start:{dateTime,timeZone}, end:…}
UPDATE … FILTER id=… PATCH /me/events/{id}
DELETE … FILTER id=… DELETE /me/events/{id}

Ohne Angabe von start_timeZone/end_timeZone setzt die Spec Europe/Berlin (defaultWrite). Vollständiges Beispiel: graph-calendar.sql.

Benutzer-Kalender (Client Credentials)

INSERT INTO user_calendar_events ("userId", subject, "start_dateTime", "end_dateTime")
VALUES ('USER-OBJECT-ID', 'Team-Meeting', '2026-07-01T10:00:00', '2026-07-01T11:00:00');

SELECT id, subject FROM user_calendar_events
  FILTER userId='USER-OBJECT-ID' AND startswith(subject,'Team-Meeting');

UPDATE user_calendar_events SET subject = 'Team-Meeting (neu)'
  FILTER userId='USER-OBJECT-ID' AND id=EVENT-ID;

DELETE FROM user_calendar_events FILTER userId='USER-OBJECT-ID' AND id=EVENT-ID;

Pfad-Parameter (userId) stehen vor dem OData-Teil im FILTER, getrennt durch AND. Vollständiges Beispiel: graph-user-calendar.sql.

6. System-Tabellen

SELECT table_name, remarks FROM system.table_list;

SELECT column_name, type_name FROM system.column_list WHERE table_name = 'users';

SELECT column_name FROM system.pk_list WHERE table_name = 'users';

7. Hinweise

  • Throttling: Graph antwortet bei Last mit HTTP 429 — Retry-After beachten.
  • 401/403: fehlende Berechtigung, abgelaufenes Token oder keine Admin-Zustimmung.
  • /me/…: nur mit delegiertem Benutzer-Token, nicht mit Client Credentials.
  • Weitere Entities: weitere Graph-Ressourcen lassen sich in graph-spec.json ergänzen — gleiches Muster (dataPath, filterParam, nextLink).