使用sqlalchemy生成java中的实体类(一)

介绍

sqlalchemy

sqlalchemy是python的第三方库,提供了SQL工具和对象关系映射(ORM)工具,类似的还有Django ORM等。

java实体类

比如有个学生类stduent,属性和方法如图所示

可以在IntelliJ IDEA中,通过快捷键alt+insert生成对应的方法。

二者的联系

java的ORM框架,比如Mybatis等,假设现在有张stduent表,定义如下

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

如果要通过Mybatis实现CRUD操作,显然需要实体类student,笔者举得例子比较简单,如果表的属性比较多或者表很多,每加入个表都需要实体类,写起来就会繁琐。因此,笔者想法是通过sqlalchemy读取表,然后生成实体类,或者说生成Studnet.java文件。

实现的过程

以student表为例。表中id的类型为int,转换成实体类则id的类型变成Integer或者int。其他也是类似的,而且实体类的实现很相似,先写属性,然后是get方法、set方法,构造函数、toString方法的实现。

既然如此,python可以通过字符串的拼接实现,但可能更繁杂,因此,笔者决定使用模板语法,使用jinja2,当然还有其他模板语法,比如Djang Template Lanage等。jinja2也是python的第三方库。

jinja2

 Jinja是一个基于Python设计语言的“全功能模板引擎”,下面简单的使用一下jinja2

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

结果如下。

具体可参考jinja2的官方文件。

欢迎来到 Jinja2 — Jinja2 2.7 documentation (jinkan.org)

正文

写之前先创建一个目录,目录名叫python_sql_java,其中创建一个settings.py文件。代码如下,

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

读取属性及类型

这一步其实可以不通过sqlalchemy,可以用pymysql,但相对来说,用sqlalchemy更好,毕竟已经是封装好的框架,不需要写sql语句。

第一种方法

在python_sql_java文件下创建get_sql.py文件。其中代码如下。

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)

打印结果如下。

也可以选择把Integer变成int类型的,或者还有其他类型的替换,差不多,具体情况具体分析。

第二种方法

通过pandas的read_sql_table方法,获取类型,实际上查不多,没什么区别,代码如下

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)

结果如下,笔者后面将使用第一种方法

创建jinja2模板

实体类的样子

在写模板之前,先看一下IntelliJ IDEA生成的实体类Student。

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 +
                '}';
    }
}

写模板前的准备

现在对上面代码进行模仿。

在模仿之前,先在当前目录(python_sql_java)下进行一个包,叫templates,其中放模板

在templates目录下进行一个entity.java文件,作为模板。

在python_sql_java目录新建一个mapper.py和render.py,

模板

根据上面实体类,entity的代码如下



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 %}
                '}';
    }
}

渲染

render.py的代码如下

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)

运行后结果如图所示

渲染后的结果还是可以的。

最终的代码

在mapper.py文件中写下最终的代码。

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')

运行后在当前目录下生成student.java文件,内容和上面的java一样。

因为这个实体的属性比较少,运行没有问题,但不一定全部适用,可以会有问题,后面会继续修改。

猜你喜欢

转载自blog.csdn.net/qq_63401240/article/details/134085923