Use sqlalchemy to generate entity classes in java (1)

introduce

sqlalchemy

sqlalchemy is a third-party library for Python that provides SQL tools and object-relational mapping (ORM) tools, similar to Django ORM.

java entity class

For example, there is a student class stduent, with attributes and methods as shown in the figure.

The corresponding method can be generated in IntelliJ IDEA through the shortcut keys alt+insert.

The connection between the two

Java's ORM framework, such as Mybatis, etc., assume that there is a stduent table, defined as follows

create table student(
    id int primary key auto_increment not null comment '学号',
    name varchar(16) not null comment '姓名',
    age int not null comment '年龄'
)

If you want to implement CRUD operations through Mybatis, you obviously need the entity class student. The example I gave is relatively simple. If the table has many attributes or there are many tables, each time you add a table, you will need an entity class, which will be cumbersome to write. Therefore, the author's idea is to read the table through sqlalchemy and then generate entity classes, or generate the Studnet.java file.

implementation process

Take the student table as an example. The type of id in the table is int. When converted into an entity class, the type of id becomes Integer or int. Others are similar, and the implementation of the entity class is very similar. The attributes are written first, and then the get method, set method, constructor, and toString method are implemented.

In this case, python can be implemented by concatenating strings, but it may be more complicated. Therefore, the author decided to use template syntax, using jinja2, and of course other template syntaxes, such as Djang Template Lanage. Jinja2 is also a third-party library for python.

jinja2

 Jinja is a "full-featured template engine" based on the Python design language. Let's briefly use jinja2

from jinja2 import Template
template = Template('hello {
   
   {text}}')
result=template.render(text='jinja2')
print(result)

The results are as follows.

For details, please refer to the official documentation of jinja2.

Welcome to Jinja2 — Jinja2 2.7 documentation (jinkan.org)

text

Before writing, create a directory named python_sql_java, and create a settings.py file in it. code show as below,

from sqlalchemy import create_engine,MetaData
# 数据库、驱动、用户、密码、ip、端口、端口号、数据库 
engine=create_engine('mysql+pymysql://root:123456@localhost:3306/sqlalchemy')  # 获得engine
# metadata对象
metadata=MetaData()

Read properties and types

In fact, this step does not need to go through sqlalchemy. You can use pymysql, but relatively speaking, it is better to use sqlalchemy. After all, it is already an encapsulated framework and there is no need to write sql statements.

the first method

Create the get_sql.py file under the python_sql_java file. The code is as follows.

from sqlalchemy import Table, inspect
from settings import engine, metadata

sql_java = {
    'Varchar': 'String',
}


def get_sql_word_type(table_name) -> list:
    """
    :param table: 表名
    :return: list
    """
    inspector = inspect(engine)
    columns = inspector.get_columns(table_name)
    words = []
    for column in columns:
        word_type = type(column['type']).__name__.capitalize()
        if word_type in sql_java.keys():
            word_type = sql_java[word_type]
        word_name = column['name']
        words.append({'word_name': word_name, 'word_type': word_type})
    return words


properties = get_sql_word_type('student')
for property in properties:
    print(property)

The print results are as follows.

You can also choose to change the Integer into an int type, or replace it with other types. It's almost the same. The specific situation will be analyzed in detail.

The second method

Get the type through the read_sql_table method of pandas. In fact, there is not much to check, and there is no difference. The code is as follows

import pandas as pd
from settings import engine

sql_java = {
    'int64': 'Integer',
    'object': 'String',
}


def get_sql_word_type(table_name):
    df = pd.read_sql_table(table_name, con=engine)
    field_info = []
    for column in df.columns:
        field_name = column
        field_type = str(df[column].dtype)
        if field_type in sql_java:
            field_type = sql_java[field_type]
        field_info.append([field_name, field_type])
    return field_info


field_info = get_sql_word_type('student')
for i in field_info:
    print(i)

The results are as follows. The author will use the first method later.

Create jinja2 template

Entity class looks like

Before writing the template, let's take a look at the entity class Student generated by IntelliJ IDEA.

public class stduent {
    private Integer id;
    private String name;
    private Integer age;

    public stduent() {
    }

    public stduent(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "stduent{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Preparation before writing template

Now imitate the above code.

Before imitating, first create a package called templates in the current directory (python_sql_java), in which the templates are placed.

Create an entity.java file in the templates directory as a template.

Create a new mapper.py and render.py in the python_sql_java directory,

template

According to the above entity class, the code of entity is as follows



public class {
   
   {table_name | capitalize}}{ {#类名#}

    {%- for property in properties %}  {# 属性 #}
    private {
   
   {property.word_type}} {
   
   {property.word_name}};
    {%- endfor %}

    public {
   
   {table_name | capitalize}}() {   {# 无参构造 #}
    }

    public {
   
   {table_name | capitalize}}({# 有参构造 #}
        {%- for property in properties -%}
            {%- if loop.last -%}
            {
   
   {property.word_type}} {
   
   {property.word_name}}
            {%- else -%}
            {
   
   {property.word_type}} {
   
   {property.word_name}},
            {%-endif-%}
        {% endfor -%}
    ) {
        {%- for property in properties %}
        this.{
   
   {property.word_name}} = {
   
   {property.word_name}};
        {%- endfor %}
    }
    {% for property in properties %}  {# get方法 #}
    public {
   
   {property.word_type}} get{
   
   {property.word_name | capitalize }}() {
        return {
   
   {property.word_name}};
    }
    {% endfor%}
    {% for property in properties %}   {# set方法 #}
    public void set{
   
   {property.word_name | capitalize }}({
   
   {property.word_type}} {
   
   {property.word_name}}) {
        this.{
   
   {property.word_name}}={
   
   {property.word_name}};
    }
    {% endfor%}
    @Override
    public String toString() {  {# toString方法 #}
        return "{
   
   {table_name | capitalize}}{" +
                {% for property in properties %}
                {%- if loop.first -%}
                "{
   
   {property.word_name}}=" + {
   
   {property.word_name}} +
                {% elif loop.last%}
                ",{
   
   {property.word_name}}=" + {
   
   {property.word_name}} +
                {%- else -%}
                ", {
   
   {property.word_name}}='" + {
   
   {property.word_name}} + '\'' +
                {%- endif -%}
                {% endfor %}
                '}';
    }
}

rendering

The code of render.py is as follows

from jinja2 import FileSystemLoader,Environment
def get_mapper(table_name,properties):
    loader = FileSystemLoader('./templates')
    environment = Environment(loader=loader)
    template = environment.get_template('entity.java')
    return template.render(
        table_name=table_name,
        properties=properties
    )

properties=[{'word_name': 'id', 'word_type': 'Integer'}, {'word_name': 'name', 'word_type': 'String'}, {'word_name': 'age', 'word_type': 'Integer'}]
java=get_mapper('student',properties)
print(java)

The result after running is as shown in the figure

The result after rendering is still OK.

final code

Write the final code in the mapper.py file.

from jinja2 import Template, FileSystemLoader, Environment
from sqlalchemy import Table, inspect
from settings import engine, metadata
import os


class Entity:
    def __init__(self, table_name):
        self.table_name = table_name
        self.sql_java = {
            'Varchar': 'String',
        }

    def get_sql_word_type(self) -> list:
        """
        :return: list
        """
        inspector = inspect(engine)
        columns = inspector.get_columns(self.table_name)
        words = []
        for column in columns:
            word_type = type(column['type']).__name__.capitalize()
            if word_type in self.sql_java.keys():
                word_type = self.sql_java[word_type]
            word_name = column['name']
            words.append({'word_name': word_name, 'word_type': word_type})
        return words

    def get_mapper(self)->str:
        words = self.get_sql_word_type()
        loader = FileSystemLoader('./templates')
        environment = Environment(loader=loader)
        template = environment.get_template('entity.java')
        return template.render(
            table_name='student',
            properties=words
        )

    def get_result(self, java_str, file_path=None):
        if not file_path:
            file_path = f'{self.table_name}.java'
            if not os.path.exists(file_path):
                open(file_path, 'w').close()
        with open(file_path, 'w') as f:
            f.write(java_str)

    @staticmethod
    def start(table_name, file_path=None):
        entity = Entity(table_name)
        java = entity.get_mapper()
        entity.get_result(java)


Entity.start('student')

After running, the student.java file is generated in the current directory, the content is the same as the java above.

Because this entity has relatively few attributes, there is no problem in running it, but it may not be applicable to all of them. There may be problems, and we will continue to modify them later.

Guess you like

Origin blog.csdn.net/qq_63401240/article/details/134085923