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/…).
java -jar restjdbc.jar graph-oauth2.sql
tenant, clientId und clientSecret ersetzenconnect 'jdbc:rest:https://graph.microsoft.com/v1.0|spec=graph-spec.json,auth=oauth2,tenant=…,clientId=…,clientSecret=…,scope=https://graph.microsoft.com/.default'
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
Mail.Read anlegentenant und clientId ersetzenconnect '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
Termine des angemeldeten Benutzers lesen und schreiben (INSERT, UPDATE, DELETE):
java -jar restjdbc.jar graph-calendar.sql
Calendars.ReadWrite in der Azure-App anlegentenant und clientId ersetzenCalendars.ReadWrite offline_access User.Readconnect '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.
Termine eines beliebigen Benutzers im Mandanten lesen und schreiben (/users/{userId}/events):
java -jar restjdbc.jar graph-user-calendar.sql
Calendars.ReadWrite anlegen und Admin-Zustimmung erteilentenant, clientId, clientSecret und USER-OBJECT-ID ersetzenhttps://graph.microsoft.com/.defaultDer 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;
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);
| 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.
| 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.
| Property | Wert | Erforderlich |
|---|---|---|
auth |
bearer |
Ja |
token |
Delegiertes Access Token | Ja |
Spec-Datei: graph-spec.json
Datei: graph-spec.json
| 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 SeitenfilterParam: FILTER-Inhalt wird als OData-$filter gesendet (native Syntax)selectParam: nur im SELECT genannte Spalten werden als $select angefordertjsonPath / selectName: verschachtelte Felder (z. B. Absender bei messages)"write": false — Schreiben ist in diesem Beispiel deaktiviert.
User.Read.AllGroup.Read.AllFür Benutzer-Kalender (user_calendar_events) zusätzlich Anwendungsberechtigung: Calendars.ReadWrite.
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.
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).
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).
SELECT id, "displayName", "mailEnabled" FROM groups
FILTER mailEnabled eq true AND securityEnabled eq true;
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).
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.
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.
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';
/me/…: nur mit delegiertem Benutzer-Token, nicht mit Client Credentials.dataPath, filterParam, nextLink).