Quantcast
Channel: Planet Python
Viewing all articles
Browse latest Browse all 23695

Zato Blog: Using Oracle Database from Python and Zato Services

$
0
0

Using Oracle Database from Python and Zato Services

Overview

Oracle Database remains a cornerstone of enterprise IT, powering mission-critical applications around the world. Integrating Oracle with Python unlocks automation, reporting, and API-based workflows. In this article, you'll learn how to:

  • Connect to Oracle Database from Python
  • Use Oracle Database in Zato services
  • Execute SQL queries and call stored procedures
  • Understand the underlying SQL objects

All examples are based on real-world use cases and follow best practices for security and maintainability.

Why Use Oracle Database from Python?

Python is a popular language for automation, integration, and data processing. By connecting Python to Oracle Database, you can:

  • Automate business processes
  • Build APIs that interact with enterprise data
  • Run analytics and reporting jobs
  • Integrate with other systems using Zato

Using Oracle Database in Zato Services

SQL connections are configured in the Dashboard, and you can use them directly in your service code.

In all the service below, the logic is split into several dedicated services, each responsible for a specific operation. This separation improves clarity, reusability, and maintainability.

Setting Up: Oracle Database Objects

First, let's start with the basic SQL objects used in our examples:

-- Table definitionCREATETABLEusers(user_idNUMBER,usernameVARCHAR2(50));
-- Sample dataINSERTINTOusers(user_id,username)VALUES(1,'john_doe');INSERTINTOusers(user_id,username)VALUES(2,'jane_smith');INSERTINTOusers(user_id,username)VALUES(3,'bob_jones');
-- Stored procedure: process_dataCREATEORREPLACEPROCEDUREprocess_data(input_numINNUMBER,input_strINVARCHAR2,output_numOUTNUMBER,output_strOUTVARCHAR2)ASBEGINoutput_num:=input_num*2;output_str:='Input was: '||input_str;ENDprocess_data;/
-- Stored procedure: get_usersCREATEORREPLACEPROCEDUREget_users(recordsetOUTSYS_REFCURSOR)ASBEGINOPENrecordsetFORSELECTuser_id,usernameFROMusersORDERBYuser_id;ENDget_users;/

1. Querying All Users

This service retrieves all users from the users table.

# -*- coding: utf-8 -*-fromzato.server.serviceimportServiceclassGetAllUsers(Service):""" Service to retrieve all users from the database."""defhandle(self):# Obtain a reference to the configured Oracle Database connectionconn=self.out.sql['My Oracle DB']# Define the SQL query to select all rows from the users tablequery='select * from users'# Execute the query; returns a list of dictionaries, one per rowresponse=conn.execute(query)# Set the service response to the query resultself.response.payload=response
[{"user_id":1,"username":"john.doe"},{"user_id":2,"username":"jane.smith"},{"user_id":3,"username":"bob.jones"}]

Explanation:

  • The service connects to Oracle using the configured connection.
  • It executes a simple SQL query to fetch all user records.
  • The result is returned as the service response payload.

2. Querying a Specific User by ID

This service fetches a user by their user_id using a parameterized query. There are multiple ways to retrieve results depending on whether you expect one or many rows.

# -*- coding: utf-8 -*-fromzato.server.serviceimportServiceclassGetUserById(Service):""" Service to fetch a user by their user_id."""defhandle(self):# Get the Oracle Database connection from the poolconn=self.out.sql['My Oracle DB']# Parameterized SQL to prevent injectionquery='select * from users where user_id = :user_id'# In a real service, this would be read from incoming JSONparams={'user_id':1}# Execute the query with parameters; returns a listresponse=conn.execute(query,params)# Set the result as the service's responseself.response.payload=response
[{"user_id":1,"username":"john.doe"}]

Explanation:

  • The service expects user_id in the request payload.
  • It uses a parameterized query to prevent SQL injection.
  • The result is always a list, even if only one row matches.

3. Calling a Stored Procedure with Input and Output Parameters

This service demonstrates how to call an Oracle stored procedure that takes input values and returns output values.

# -*- coding: utf-8 -*-# Zatofromzato.common.oracledbimportNumberIn,NumberOut,StringIn,StringOutfromzato.server.serviceimportServiceclassCallProcessData(Service):""" Service to call a stored procedure with input/output params."""defhandle(self):# Obtain Oracle Database connectionconn=self.out.sql['My Oracle DB']# Prepare input parameter for NUMBERin_num=NumberIn(333)# Prepare input parameter for VARCHAR2in_str=StringIn('Hello')# Prepare output parameter for NUMBER (will be written to by the procedure)out_num=NumberOut()# Prepare output parameter for VARCHAR2, specify max buffer size (optionally)out_str=StringOut(size=200)# Build the parameter list in the order expected by the procedureparams=[in_num,in_str,out_num,out_str]# Call the stored procedure with the parametersresponse=conn.callproc('process_data',params)# Return the output values as a dictionary in the responseself.response.payload={'output_num':out_num.get(),'output_str':out_str.get()}
{"output_num":666,"output_str":"Input was: Hello"}

Explanation:

  • The service prepares input and output parameters using helper classes.
  • It calls the process_data procedure with both input and output arguments.
  • The result includes both output values, returned as a dictionary.
  • Note that you always need to provide the parameters for the procedure in the same order as they were declared in the procedure itself.

4. Calling a Procedure Returning Multiple Rows

This service calls a procedure that returns a set of rows (a cursor) and collects the results.

# -*- coding: utf-8 -*-fromzato.common.oracledbimportRowsOutfromzato.server.serviceimportServiceclassCallGetUsers(Service):""" Service to call a procedure returning a set of rows."""defhandle(self):# Get Oracle Database connectionconn=self.out.sql['My Oracle DB']# Prepare a RowsOut object to receive the result setrows_out=RowsOut()# Build parameter list for the procedureparams=[rows_out]# Call the procedure, populating rowsconn.callproc('get_users',params)# Convert the cursor results to a list of rowsrows=list(rows_out.get())# Return the list as the service responseself.response.payload=rows
[[1,"john.doe"],[2,"jane.smith"],[3,"bob.jones"]]

Explanation:

  • The service prepares a RowsOut object to receive the rows into. That is, the procedure will write rows into this object.
  • It calls the get_users procedure, which populates the rows.
  • You call rows_out.get to get the actual rows from the database.
  • The rows are converted to a list and returned as the payload.

4. Returning a Single Object

When you know your query will return a single row, you can use conn.one or conn.one_or_none for more predictable results:

# -*- coding: utf-8 -*-fromzato.server.serviceimportServiceclassGetSingleUserById(Service):""" # Service to fetch exactly one user or raise if not found/ambiguous."""defhandle(self):# Get the Oracle Database connectionconn=self.out.sql['My Oracle DB']# Parameterized SQL queryquery='select * from users where user_id = :user_id'# In a real service, this would be read from incoming JSONparams={'user_id':1}# conn.one returns a dict if exactly one row, else raises (zero or multiple rows)result=conn.one(query,params)# Return the single user as the responseself.response.payload=resultclassGetSingleUserOrNoneById(Service):""" Service to fetch one user, None if not found, or raise an Exception if ambiguous."""defhandle(self):# Get Oracle Database connectionconn=self.out.sql['My Oracle DB']# SQL with named parameterquery='select * from users where user_id = :user_id'# Extract user_id from payloadparams={'user_id':1}# conn.one_or_none returns a dict if one row, None if zero, raises if multiple rowsresult=conn.one_or_none(query,params)# Return dict or Noneself.response.payload=result

Explanation:

  • conn.one(query, params) will return a single row as a dictionary if exactly one row is found. If no rows or more than one row are returned, it raises an exception.
  • conn.one_or_none(query, params) will return a single row as a dictionary if one row is found, None if no rows are found, but still raises an exception if more than one row is returned.
  • Use these methods when you expect either exactly one or zero/one results, and want to handle them cleanly.

Key Concepts Explained

  • Connection Management: Zato handles connection pooling and configuration for you. Use self.out.sql['My Oracle DB'] to get a ready-to-use connection.
  • Parameterized Queries: Always use parameters (e.g., :user_id) to avoid SQL injection and improve code clarity.
  • Calling Procedures: Use helper classes (NumberIn, StringIn, NumberOut, StringOut, RowsOut) for input/output arguments and recordsets.
  • Service Separation: Each service is focused on a single responsibility, making code easier to test and reuse.

Security and Best Practices

  • Always use parameterized queries for user input.
  • Manage credentials and connection strings securely (never hardcode them in source code).
  • Handle exceptions and database errors gracefully in production code.
  • Use connection pooling (Zato does this for you) for efficiency.

Summary

Integrating Oracle Database with Python and Zato services gives you powerful tools for building APIs, automating workflows, and connecting enterprise data sources.

Whether you need to run queries, call stored procedures, or expose Oracle data through REST APIs, Zato provides a robust and Pythonic way to do it.

More resources

➤ Python API integration tutorials
What is an integration platform?
Python Integration platform as a Service (iPaaS)
What is an Enterprise Service Bus (ESB)? What is SOA?
Open-source iPaaS in Python


Viewing all articles
Browse latest Browse all 23695

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>