Java:SQLクエリを自動生成

この記事では、Javaクラスとオブジェクトに基づいてSQLクエリを自動的に生成するためのフレームワークの作成について説明します。 すでに多くの同様の既製のソリューションがあることを理解していますが、これを自分で実装したいと考えました。



フレームワークを作成するには、JavaアノテーションとJava Reflection APIを使用します。



それでは始めましょう。





いくつかのユースケースから始めましょう。



例1



特定のPersonクラスがあるとしましょう:



public static class Person { public String firstName; public String lastName; public int age; }
      
      





次の呼び出しは、このクラスに基づいてテーブルを作成するSQLクエリを生成します。



 System.out.println(MySQLQueryGenerator.generateCreateTableQuery(Person.class));
      
      





実行すると、コンソールに次の出力が表示されます。



 CREATE TABLE `Person_table` ( `firstName` VARCHAR(256), `lastName` VARCHAR(256), `age` INT);
      
      





例2



これで、注釈を使用した例はより複雑になります。



 @IfNotExists //   CREATE- IF NOT EXISTS @TableName("persons") //    public static class Person { @AutoIncrement //   AUTO_INCREMENT @PrimaryKey //      PRIMARY KEY public int id; @NotNull //   NOT NULL public long createTime; @NotNull public String firstName; @NotNull public String lastName; @Default("21") //    public Integer age; @Default("") @MaxLength(1024) //  VARCHAR public String address; @ColumnName("letter") //    public Character someLetter; }
      
      





このクラスに基づいて、次のSQLクエリを取得します。



 CREATE TABLE IF NOT EXISTS `persons` ( `id` INT AUTO_INCREMENT, `createTime` BIGINT NOT NULL, `firstName` VARCHAR(256) NOT NULL, `lastName` VARCHAR(256) NOT NULL, `age` INT DEFAULT '21', `address` VARCHAR(1024) DEFAULT '', `letter` VARCHAR(1), PRIMARY KEY (`id`));
      
      





例3



また、データベースサーバーに接続し、そこに生成されたSQLクエリを送信できるMySQLClientクラスも作成しました。



クライアントには、 createTablealterTableinsertupdateselectのメソッドが含まれています



おおよそ次のように使用されます。



 MySQLClient client = new MySQLClient("login", "password", "dbName"); client.connect(); //    client.createTable(PersonV1.class); //   client.alterTable(PersonV1.class, PersonV2.class); //   PersonV2 person = new PersonV2(); person.createTime = new Date().getTime(); person.firstName = "Ivan"; person.lastName = "Ivanov"; client.insert(person); //     person.age = 28; person.createTime = new Date().getTime(); person.address = "Zimbabve"; client.insert(person); person.createTime = new Date().getTime(); person.firstName = "John"; person.lastName = "Johnson"; person.someLetter = 'i'; client.insert(person); List selected = client.select(PersonV2.class); //      System.out.println("Rows: " + selected.size()); for (Object obj: selected) { System.out.println(obj); } client.disconnect(); //   
      
      





仕組み



最初に、アルゴリズムはReflection APIを使用して、クラスのすべてのパブリックフィールドと非静的フィールドを反復処理します。 この場合のフィールドがアルゴリズムでサポートされている型(すべてのプリミティブデータ型、オブジェクト類似体、String型がサポートされている)の場合、データベースオブジェクトフィールドに関するデータを含むColumnオブジェクトがFieldオブジェクトから作成されます。 Javaデータ型とMySQL型の間の変換は自動的に行われます。 また、フィールドとクラスの注釈から、テーブルとそのフィールドのすべての修飾子が抽出されます。 次に、すべてのColumnからSQLクエリが形成されます。



 public static String generateCreateTableQuery(Class clazz) throws MoreThanOnePrimaryKeyException { List<Column> columnList = new ArrayList<>(); Field[] fields = clazz.getFields(); //     for (Field field: fields) { int modifiers = field.getModifiers(); if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { //  public   static Column column = Column.fromField(field); //  Field  Column if (column!=null) columnList.add(column); } } /*   Column   */ } /***************************/ public static Column fromField(Field field) { Class fieldType = field.getType(); //     ColumnType columnType; if (fieldType == boolean.class || fieldType == Boolean.class) { columnType = ColumnType.BOOL; } /*     */ { } else if (fieldType==String.class) { columnType = ColumnType.VARCHAR; } else { //       return null; } Column column = new Column(); column.columnType = columnType; column.name = field.getName(); column.isAutoIncrement = field.isAnnotationPresent(AutoIncrement.class); /*    */ if (field.isAnnotationPresent(ColumnName.class)) { //      ColumnName columnName = (ColumnName)field.getAnnotation(ColumnName.class); String name = columnName.value(); if (!name.trim().isEmpty()) column.name = name; } return column; }
      
      





同様に、ALTER TABLE、INSERT、およびUPDATEクエリが生成されます。 後者の2つの場合、列リストに加えて、そのフィールドの値もオブジェクトから抽出されます。



 Column column = Column.fromField(field); if (column!=null) { if (column.isAutoIncrement) continue; Object value = field.get(obj); if (value==null && column.hasDefaultValue) continue; //   :             if (column.isNotNull && value==null) { throw new NotNullColumnHasNullValueException(); } String valueString = value!=null ? "'" + value.toString().replace("'","\\'") + "'" : "NULL"; String setValueString = "`"+column.name+"`="+valueString; valueStringList.add(setValueString); }
      
      





フレームワークにはResultSetExtractorクラスもあり、そのメソッドであるextractResultSet(ResultSet resultSet、Class clazz)は、resultSetからclazzクラスのオブジェクトのリストを自動的に作成します。 これは非常に簡単に行われるため、ここではその動作の原理について説明しません。



github では、フレームワークの完全なソースコードを見ることができます。 これで私はすべてを持っています。




All Articles