Trust Codda or your objects?

Headless Stored Objects: A Simple Example of Working with Caché Objects in ObjectScript and Python

Neuschwanstein Castle

In June 2020, exactly 50 years of tabular data warehouses or speaking formally the relational data model. Here is an official document - the most famous article . For what we say thank you so much to Dr. Edgar Frank Codd. And by the way, the relational data model is on the list of the most important global innovations of the last 100 years, according to Forbes.

On the other hand, oddly enough, Codd considered relational databases and the SQL language to be a distorted implementation of his theory. As a guide, he even developed 12 rules that each relational database management system should satisfy (in fact, these are 13 rules). And, to tell the truth, for today, there is no way in the world to find DBMSs satisfying at least Codd's “Rule 0” and, therefore, no one can call their DBMS 100% relational :) Maybe there are exceptions, tell me?

The relational model is not very complex and has been studied far and wide. It may even be too deeply studied. Meanwhile, in this year 2019, we will also celebrate another anniversary - exactly 10 years ago, the hashtag #NoSQL, it later also “not only SQL” appeared on Twitter and began its rapid penetration into the practice of developing database models.

Why such a long preface? The fact that the programming universe consists of data (and algorithms of course), and the monopolistic position of the relational model leads to a split in the consciousness of the programmer. Because the work objects in the developer’s head (OOP are also total, right?), All these lists, queues, trees, heaps, dictionaries, streams, and so on to infinity, are far from being a table.

And if you continue and recall the storage architecture in modern DBMS? Let's talk straight, nobody in their right mind stores data in the form of tables. DBMS developers often use B-tree variants (in PostrgeSQL, for example) or, what happens much less often, dictionary-based storage. On the other hand, barricades, developers using DBMS for storage, also operate on non-tables. And this forces programmers to bridge the semantic gap with the help of a clumsy intermediate data layer. And, thereby, cause in itself internal dichotomous tension, system discomfort and debug insomnia.

That is, if briefly, there is a contradiction - the data is packed into the appropriate objects for the task, and when saved, it should take care of some tables.

Hopelessness? No :) But what about the object-relational mapping, is it in the common people of ORM? Let's leave this holy war to Yegor Bugaenko and his comrades. And this whole story from the last century, as according to the version of Uncle Bob, should not worry us .

Certainly worth mentioning that the "bag of bytes" (Robert Martin "Pure Architecture") can be serialized and thrown into a file or pushed into some other suitable stream. But, firstly, it will immediately limit us in the language, and secondly, we will now only worry about storage in the DBMS.

In these twists and turns with “bags with bytes” there is a pleasant exception - the Intersystems Caché DBMS (and now the InterSystems IRIS data platform). This is perhaps the only DBMS in the world that does not hide the obvious from the developer and even goes further - it frees you from the idea of ​​“how to keep everything properly”. Suffice it to say that the class continues the Persistent genus and is in the bag , that is, in globals (not to be confused with global variables!).

You can store all types of data, including character and binary streams. Here is the simplest example:

// класс для сохраняемого объекта с одним свойством-строкой
// сюрприз: именовать свойства можно любой строкой и, в этом примере потребовались кавычки, для идентификаторов без пробелов они, конечно, не требуются
Class FW.Events Extends %Persistent { 
    Property "My name" As %String;
// пробуем в работе через терминал
// создаём новый «чистый» объект
set haJS = ##class(FW.Events).%New()
// сохраняем его
write haJS.%Id()

Moreover, and this is great, you can communicate with stored objects not only in ObjectScript, native to Caché, but by receiving and saving them directly in programs in Python, Java, JavaScript, C ++, C #, Perl. And even, oh, horror :). it is also possible to draw information from the same objects directly through SQL queries, and it is also possible to call own methods of objects. More precisely, the methods in this case by themselves (and the magic word SqlProc) turn into stored procedures. All the magic is already under the hood of the Caché DBMS.
How to get free test access to Intersystems Caché DBMS?
It is absolutely real, that would not say evil tongues! :) You can download and install the single-user full-featured version of Caché here (you will need to register for free). Builds are available for MacOS, Windows and Linux.
The most convenient way is to work with ObjectScript code and direct access directly to the Caché DBMS server (and the InterSystems IRIS platform too) using IDE Atelier, which is based on Eclipse. All download and installation instructions are here .

To whom it is more convenient and familiar, you can use a comfortable and simple Visual Studio Code, complementing it with community-developed plug-in ObjectScript.

And now a few practical examples. Let's try to create a pair of related objects and work with them in ObjectScript and in Python. Integration with other languages ​​implemented very similar. Python is chosen from considerations of “maximum relationship” with ObjectScript — both are scripting languages, support OOP and do not have strict typing :)

For ideas for examples, we turn to vigorous Khabarovsk (not to be confused with Habrovsk!) Projects "Framework-gathering." The ideological source code lies on And our source code is lower in the text.
An important nuance for macOS users. When running Python support modules, you need to remember that you need to specify the path DYLD_LIBRARY_PATH to the directory where you installed Caché. For example:
export DYLD_LIBRARY_PATH = / application / Cache / bin: $ DYLD_LIBRARY_PATH
In the documentation, this is indicated .

Create stored classes in ObjectScript

So let's go. Classes in Caché will be very simple. You can do without the IDE - copy the code of the classes directly through the portal of your instance of the Caché platform (yes, the Caché DBMS is far from just the DBMS): System Explorer> Classes> Import (Namespace USER).

Objects after saving appear in globals with names that match the names of the corresponding classes. Search also in the Caché management portal: Systemspace Browser> Globals (Namespace USER).

// объект событие включает название, описание, дату проведения и список участников
Class FW.Event Extends %Persistent
    Property title as %String;
    Property description as %String;
    Property date as %Date;
    Property visitors as list of FW.Attendee;
//  объект участник имеет имя/ник
Class FW.Attendee Extends %Persistent 
    Property name As %String;

Accessing objects in Caché from Python

First, connect to the database database Caché. We repeat as in the documentation .
Just a useful fact for work. The free, it’s educational version of the Caché DBMS will allow you to do everything that is available in the full-featured version, but allows only two active connections. Therefore, at the same time keep the connection from the IDE and try to run another code to interact with the server will not succeed. The simplest solution found is to close the IDE while the Python code is running.
# импорт модуля Caché для интеграции с Python3import intersys.pythonbind3
# соединение с сервером
conn = intersys.pythonbind3.connection()
conn.connect_now("localhost[1972]:USER","_SYSTEM","SYS", None)
# проверка дескриптора подключенияprint ("conn = %d " % conn.handle)
# подключение к базе данных
database = intersys.pythonbind3.database(conn)

And now we will make an object database of IT events and their participants. Very, very simple. First we will create a class for registering and storing information about the event participant. For simplicity to class only the name of the participant.

# класс для объектов с информацией о зарегистрированных участникахclassAttendee:# инициализация нового пустого объекта в оперативной памятиdef__init__(self):
        self.att = database.create_new("FW.Attendee", None)
    # запись имени участника и сохранение объекта в базе данных с присвоением уникального iddefnew(self, name):
        self.att.set("name", name)
    # загрузка объекта по id из базы данных участниковdefuse(self, id):
        self.att = database.openid("FW.Attendee",str(id),-1,-1)
    # удаление объект из базы данных участниковdefclean(self):
        id = self.att.run_obj_method("%Id",[])
        self.att.run_obj_method("%DeleteId", [id])

As you can see, we use ready-made wrapper functions for accessing fields of objects in Caché: set and in the parameters we pass the property name in quotes and openid with the name of the package and the class. About the similar function get there are examples below. For access to any other methods, including those inherited by the class from ancestors, the run_obj_method () function is used with the method name and call parameters, if needed.

The most important magic in the line: self.att.run_obj_method ("% Save", [])
This is how we can save objects directly and without the need to use additional libraries and frameworks / frameworks, such as the omnipresent and unsightly ORM.

In addition, given the object-oriented nature of ObjectScript, along with the methods of our class (in our example, we did not do them), we gain access from Python to the entire set of methods inherited from the Persistent class and its ancestors as a bonus. Here is a complete list , if that.

Create the first participant:

att = Attendee()"Аким")

After running this code, a global with the name FW.AttendeeD and the contents of the object just saved will appear in the database as in the screenshot:

This object has its own id (with the number 1) after saving. Therefore, you can download it into our program by this id:

att = Attendee()
print (att.att.get("name"))

And now, again, knowing id, if necessary, you can delete an object from the database of participants:

att = Attendee()

Check, after running this example, the object record should disappear in the global. Although the loaded data is still “in memory” of your object until the program ends.

Take the next step. Create your own event records.

# класс для объектов с информацией о мероприятияхclassEvent:# инициализация нового пустого объекта в оперативной памятиdef__init__(self):
        self.event = database.create_new("FW.Event", None)
    # наполнение объекта информацией и сохранение в базе данных с присвоением уникального iddefnew(self, title, desc, date):
        self.event.set("title", title)
        self.event.set("description", desc)
        self.event.set("date", date)
    # загрузка объекта по id из базы данныхdefuse(self, id):
        self.event = database.openid("FW.Event",str(id),-1,-1)
    # добавление участника в список участников мероприятияdefaddAttendee(self, att):
        eventAtt = self.event.get("visitors")
        eventAtt.run_obj_method("Insert", [att])
        self.event.set("visitors", eventAtt)
    # удаление объекта из базы данныхdefclean(self):
        id = self.event.run_obj_method("%Id",[])
        self.event.run_obj_method("%DeleteId", [id])

The class structure is almost the same as the class for the participant. Most importantly, the addAttendee (att) method of adding participants to the list of participants for this event has appeared.

We try to create an object-record about a new event and save it in the database:

haJS = Event()"haJS", "Фронтенд митап", "2019-01-19")

It should look like this (note that the date is automatically converted to ObjectScript and when loaded back to the Python object will be returned in the originally specified format): It

remains to add the participant to the event:

# загружаем ранее сохранённое мероприятие
haJS = Event()
# создаём нового участника
att = Attendee()"Марк")
# добавляем участника в наше мероприятие

So, these examples show that it is not necessary to think simultaneously about your data model and its tabular storage scheme. You can use more obvious tools for your tasks.

Detailed instructions on how to connect and use Caché with Python and other languages ​​are always available to you in the documentation and on the InterSystems developer community portal - this is not a few, and 5,000 members of
Reference: InterSystems Caché multi-model DBMS remains the undisputed world leader in object databases.

Also popular now: