0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

JPAのEntityクラスファイルを自動生成するソースを作ってみた

Last updated at Posted at 2020-10-11

##はじめに
タイトルにもある通り、今回はEntityクラスのJavaファイルを生成するアプリを作ってみました!
OracleDB11g2R,MySQLでは動作確認済みです。
※下図の通り、結構簡単な物しか生成できませんが。。
(諸事情で@column@Id@TableはJavaEEのではなく、自作したものを使用しました)

Test.java
import java.util.ArrayList;

import annotation.Entity;
import annotation.Table;
import annotation.column;
import annotation.id;

@Entity
@Table("test")//DBに登録したテーブル名が表示されます
public class Test {

	@id//主キー
	@column//カラム
	private String num;

	@column//カラム
	private String namename;

	public void setNum(String num) {
		this.num = num;
 	}

	public String getNum() {
		return this.num;
 	}

	public void setNamename(String namename) {
		this.namename = namename;
 	}

	public String getNamename() {
		return this.namename;
 	}

}

##環境(適当)

  • 実行環境: macOS
  • IDE: Eclipse

##構成(超概要)

クラス名 説明
EntityGenerator 下記クラスを呼び出しながら、JavaFileの生成及びファイルの中身をWriteしていくクラス
DBReader DBに接続し、メタデータを取得してくるクラス(テーブル一覧、カラム名、主キーetc..)
DataTypeMapper カラムのデータ型をDBのデータ型 → javaのデータ型に変換するクラス
EntityInfo DBReaderで取得してきた情報を格納するクラス
annotation群 @Table, @Id, @column
 
##ソース

###EntityGenerator

interface

EntityGenerator.java

package entityCreater.generator;

public interface EntityGenerator {

	void generateEntity();

}

実装クラス

EntityGeneratorImplements.java

package entityCreater.generator;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import entityCreater.info.EntityInfo;
import entityCreater.reader.DBReaderImplements;

public class EntityGeneratorImplements implements EntityGenerator{

	//Fileを保存する場所
	String filePlace;

	/**
	 * コンストラクタ
	 */
	public EntityGeneratorImplements(String filePlace) {
		this.filePlace = filePlace;
	}

	/**
	 * Entity作成メソッド
	 */
	@Override
	public void generateEntity() {
		System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
		System.out.println("EntityFile作成メソッドを開始します");

		ArrayList<EntityInfo> entityInfos = new DBReaderImplements().read();
		for(EntityInfo ei : entityInfos) {
			createFile(ei);
		}
	}


	/**
	 * EntityFile作成メソッド
	 * @param entityInfo
	 */
	private void createFile(EntityInfo entityInfo) {
    	//DBのテーブル情報を取得
    	String tableName = entityInfo.getTableName();
    	String id = entityInfo.getId();
    	List<String[]> columns = entityInfo.getColumns();

    	//クラス名を生成
    	String className = tableName.substring(0, 1).toUpperCase() + tableName.substring(1).toLowerCase();

		//EntityFileクラス
		File entityFile = new File(filePlace + "/" + className + ".java");

		try{
			if (entityFile.createNewFile()){
				System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
		    	System.out.println("ファイルの作成に成功しました。");
		    }else{
		    	System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
		    	System.out.println("ファイルの作成に失敗しました");
		    }
		}catch(IOException e){
		    System.out.println(e);
		}

		//Fileの中身を作成する
		System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
    	System.out.println("ファイルの中身を作成します。");

    	try(PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(entityFile, true)));) {

    		//Annotationのインポート文
    		pw.println("import annotation.Entity;");
    		pw.println("import annotation.Table;");
    		pw.println("import annotation.id;");
    		pw.println("import annotation.column;");
    		pw.println("");

    		//Class宣言(開始)
    		pw.println("@Entity");
    		pw.println("@Table(\"" + tableName + "\")");
    		pw.println("public class " + className + " {");
    		pw.println("");

    		//カラム宣言
    		Iterator<String[]> it = columns.iterator();

    		while(it.hasNext()) {
    			String[] colum = it.next();

    			//Columnの宣言
    			if(colum[1].equals(id)) {
    	    		pw.println("	@id");
    			}
    			pw.println("	@column");
        		pw.println("	private " + colum[0] + " " + colum[1] + ";");
        		pw.println("");

    		}

    		//Setter.Getter宣言
    		//Iteratorを先頭に戻す
    		it = columns.iterator();

    		while(it.hasNext()) {
    			String[] colum = it.next();

    			//Setter宣言
    			pw.println("	public void set" + colum[1].substring(0, 1).toUpperCase() + colum[1].substring(1).toLowerCase() + "(" + colum[0] + " " + colum[1] + ") {");
    			pw.println("		this." + colum[1] + " = " + colum[1] + ";");
    			pw.println(" 	}");
    			pw.println("");

    			//Getter宣言
    			pw.println("	public " + colum[0] + " get" + colum[1].substring(0, 1).toUpperCase() + colum[1].substring(1).toLowerCase() + "() {");
    			pw.println("		return this." + colum[1] + ";");
    			pw.println(" 	}");
    			pw.println("");
    		}

    		//Class宣言(終了)
    		pw.println("}");

			//終了表示
			System.out.println(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
	    	System.out.println("*************************************************************");
	    	System.out.println("***    SUCCESS   パッケージ宣言、多重度の宣言を追記してください。 ***");
	    	System.out.println("*************************************************************");

		} catch (IOException e) {
			e.printStackTrace();
		}finally {

		}
	}


}

###DBReader
interface

DBReader.java
package entityCreater.reader;

import java.util.ArrayList;

import entityCreater.info.EntityInfo;

public interface DBReader {

	ArrayList<EntityInfo> read();
}

実装クラス

DBReaderImplements.java
package entityCreater.reader;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Iterator;

import container.DBConfig;
import container.EnvironmentConfigReader;
import db_access.DBAccess;
import entityCreater.dataTypeMapper.DataTypeMapper;
import entityCreater.info.EntityInfo;

public class DBReaderImplements implements DBReader{

	@Override
	public ArrayList<EntityInfo> read() {

		//metadata格納用
		ResultSet rs = null;

		//Table情報格納用
		ArrayList<String> tableNames = new ArrayList<>();

		//Entity情報格納用
		ArrayList<EntityInfo> entityInfos = new ArrayList<>();

		//DB設定の取得
                /*
          * 以下の範囲はあまり気にしないでください。
          * 自作のyamlファイルからDBの接続情報(URLとかPASSWORDとか)を取得してきて、
          * DBAccessクラス内部でDBとのConnectionを取得しているだけです。
          * EnvironmentConfigReaderとDBAccessのソースは参考に記載しておきます
          */

               /*
               *ここから
               */
		EnvironmentConfigReader ecr = new EnvironmentConfigReader();
		DBConfig dbc = ecr.read();

		String dbName = dbc.getDbName();

		//コネクションの確立
		DBAccess dba = new DBAccess(dbc);

               /*
               * ここまで
               */

		try {

			// データベースとの接続
			Connection con = dba.getConnection();

			//データベースメタデータの取得
			DatabaseMetaData dbmd = con.getMetaData();

			//Table名の取得
			System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
			System.out.println("Table一覧を取得します");
			String[] types = { "TABLE", "VIEW", "SYSTEM TABLE" };
			rs = dbmd.getTables(dbName, null, "%", types);
			while (rs.next()) {
				String tableName = rs.getString("TABLE_NAME");

				//Table名の格納
				tableNames.add(tableName);
			}

			//Table情報の格納用クラス群
			Iterator<String> tableNameList = tableNames.iterator();
			String tableName;
			String id = null;
			ArrayList<String[]> columns;

			while (tableNameList.hasNext()) {

				//TableNameを取得
				tableName = tableNameList.next();

				System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
				System.out.println("Table名:" + tableName + "のEntityクラスを生成します");

				//Tableの全column情報の取得
				System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
				System.out.println("Table名:" + tableName + "の全column情報を取得します");
				rs = dbmd.getColumns(dbName, null, tableName, "%");

				//column格納用
				columns = new ArrayList<>();
				while (rs.next()) {
					//SQLのデータタイプからJavaのデータタイプに変換
					String dataType = DataTypeMapper.dataTypeChange(rs.getString("TYPE_NAME"));

					//変数名を小文字に変換
					String columnName = rs.getString("COLUMN_NAME").toLowerCase();

					String[] column = { dataType, columnName };
					columns.add(column);
				}

				//PrimaryKeyを取得
				System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
				System.out.println("Table名:" + tableName + "のPrimaryKeyを取得します");
				ResultSet primaryKeys = dbmd.getPrimaryKeys(dbName, null, tableName);
				if (primaryKeys.next()) {
					System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
					System.out.println("Table名:" + tableName + "のPrimaryKeyは " + primaryKeys.getString("COLUMN_NAME") + "です");
					id = primaryKeys.getString("COLUMN_NAME").toLowerCase();
				}

				System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
				System.out.println("EntityInfoクラスにTable情報を格納しています");
				//Table情報格納用クラス
				EntityInfo ei = new EntityInfo();

				//Table情報格納用クラスにTableNameを設定
				ei.setTableName(tableName);
				//Table情報格納用クラスにidを設定
				ei.setId(id);
				//Table情報格納クラスにカラム情報を設定
				ei.setColumns(columns);

				entityInfos.add(ei);
			}

			rs.close();

			// データベースのクローズ
			System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
			System.out.println("DBとのコネクションを破棄します");
			con.close();

		} catch (Exception e) {
			System.out.println("Exception発生");
			e.printStackTrace();
		}
		return entityInfos;
	}

}

###DataTypeMapper

DataTypeMapper.java

package entityCreater.dataTypeMapper;

public class DataTypeMapper {

	/**
	 * SQLのデータタイプからJavaのデータタイプに変換するクラス
	 * @param sqlDataType
	 * @return
	 */
	public static String dataTypeChange(String sqlDataType) {

		String javaDataType = null;

		switch (sqlDataType) {
		case "CHAR":
		case "VARCHAR":
		case "LONGVARCHAR":
			javaDataType = "String";
			break;
		case "NUMERIC":
		case "DECIMAL":
			javaDataType = "java.math.BigDecimal";
			break;
		case "BIT":
			javaDataType = "boolean";
			break;
		case "TINYINT":
			javaDataType = "byte";
			break;
		case "SMALLINT":
			javaDataType = "short";
			break;
		case "INTEGER":
		case "INT":
			javaDataType = "Integer";
			break;
		case "BIGINT":
			javaDataType = "long";
			break;
		case "REAL":
			javaDataType = "float";
			break;
		case "FLOAT":
		case "DOUBLE":
			javaDataType = "double";
			break;
		case "BINARY":
		case "VARBINARY":
		case "LONGVARBINARY":
			javaDataType = "byte[]";
			break;
		case "DATE":
			javaDataType = "java.sql.Date";
			break;
		case "TIME":
			javaDataType = "java.sql.Time";
			break;
		case "TIMESTAMP":
			javaDataType = "java.sql.Timestamp";
			break;
		default:
			break;
		}

		return javaDataType;

	}

}

###EntityInfo

EntityInfo.java

package entityCreater.info;

import java.util.List;

/**
 *
 * Entity情報格納クラス
 * @author okamotoyuuma
 *
 */
public class EntityInfo {

	//テーブル名
	private String tableName;

	//Primary Key名
	private String id;

	//すべてのカラム名と型
	private List<String[]> columns;

	/**
	 * (non-javadoc)
	 * getter setter
	 * @return
	 */

	public String getTableName() {
		return tableName;
	}

	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	public String getId() {
		return id;
	}

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

	public List<String[]> getColumns() {
		return columns;
	}

	public void setColumns(List<String[]> columns) {
		this.columns = columns;
	}

}

###アノテーション
@Tableアノテーション(属性にテーブル名を格納するクラス)

Table.java
package annotation;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Target(TYPE)
@Retention(RUNTIME)
public @interface Table {

	String value();

}

@idアノテーション(主キーのフィールドに付与する)

id.java
package annotation;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(FIELD)
public @interface id {

}

@columnnアノテーション(テーブルのカラム値に付与する)

column.java
package annotation;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(FIELD)
public @interface column {

}

##使い方
適当にMainメソッドを作って、下記のように生成するEntityクラスを格納する場所を指定すればOKです!

main.java

public class main {

	public static void main(String[] args) {
               EntityGenerator eg = new EntityGeneratorImplements("保存先のパス");
               eg.generateEntity();
	}

}

##最後に
今回はEntityクラスの自動生成をしてみました。
ソースやロジック等はまだまだ未熟で稚拙ですが、File生成はしたことがなかったのでとても良い経験になりました!
考慮できていない点や、ソースの改善点などご指摘あればコメントでいただけますと幸いです。

以下の参考項に上記ソースに載せていないDB設定ファイルや読み込むクラス等を載せていますので
興味ある方はみていただけたらと思います。

##参考(DB設定YAMLファイルと設定ファイルを読み込むクラスなど)

DB設定ファイル

DBProfile.yaml

#DBの設定
!!container.DBConfig #DB定義設定クラスのパス
driver: com.mysql.cj.jdbc.Driver #driver名
url: jdbc:mysql://localhost:3306/DB名 #DBのURL
user: DBのユーザ名 #DBのユーザ
password: password #DBのパスワード
numberOfAccess: 10 #コネクションの数
dbName: データベース名 #DB名
dbType: MySQL #DBの種類(今回対応するのはMySQLとOracleDBのみ)
schema: スキーマ名

※DB設定ファイルは別のアプリでも使っているため、余計な情報も入ってます。。

設定情報を格納するためのクラス

DBConfig.java

package container;

//データベースの設定ファイル
public class DBConfig {

	//ドライバ名
	String driver;

	//DBのURL
	String url;

	//DBのユーザ
	String user;

	//パスワード
	String password;

	//スキーマ名
	String schema;

	//コネクションの確立数
	int numberOfAccess;

	//DBName
	String dbName;

	//DBType
	String dbType;

	public String getDriver() {
		return driver;
	}

	public void setDriver(String driver) {
		this.driver = driver;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUser() {
		return user;
	}

	public void setUser(String user) {
		this.user = user;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getSchema() {
		return schema;
	}

	public void setSchema(String schema) {
		this.schema = schema;
	}

	public int getNumberOfAccess() {
		return numberOfAccess;
	}

	public void setNumberOfAccess(int numberOfAccess) {
		this.numberOfAccess = numberOfAccess;
	}

	public String getDbName() {
		return dbName;
	}

	public void setDbName(String dbName) {
		this.dbName = dbName;
	}

	public String getDbType() {
		return dbType;
	}

	public void setDbType(String dbType) {
		this.dbType = dbType;
	}

}


上記設定ファイルを読み込むEnvironmentConfigReaderクラス

EnvironmentConfigReader.java

package container;

import org.yaml.snakeyaml.Yaml;

public class EnvironmentConfigReader implements Reader<DBConfig>{

	//DBの設定ファイル
	static String configFileName = "DBProfile.yaml";

	//yamlファイルからDB設定を取得するメソッド(引数なし)
	@Override
	public DBConfig read() {
		//ログ発生箇所
		System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
		//処理内容
		System.out.println(configFileName + "の読み込みを開始します。");

		Yaml yaml = new Yaml();
		DBConfig dbc = (DBConfig) yaml.load(getClass().getResourceAsStream(configFileName));

		//ログ発生箇所
		System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
		//処理内容
		System.out.println(configFileName + "の読み込みが完了しました。");

		return dbc;
	}

	//yamlファイルからDB設定を取得するメソッド(引数あり)
	@Override
	public DBConfig read(String setConfigFileName) {
		//ログ発生箇所
		System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
		//処理内容
		System.out.println(configFileName + "の読み込みを開始します。");

		//指定されたファイル名をセット
		if (configFileName != null) {
			configFileName = setConfigFileName;
		}

		Yaml yaml = new Yaml();
		DBConfig dbc = (DBConfig) yaml.load(getClass().getResourceAsStream(configFileName));

		//ログ発生箇所
		System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
		//処理内容
		System.out.println(configFileName + "の読み込みが完了しました。");

		return dbc;
	}

}

DBとのコネクションを格納するクラス

DBAccess.java

package db_access;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import container.DBConfig;

public class DBAccess {

	//メンバ
	private Connection conn = null;

	public DBAccess(DBConfig dbc) {
		try {
			Class.forName(dbc.getDriver());
			conn = DriverManager.getConnection(dbc.getUrl(), dbc.getUser(), dbc.getPassword());
			System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
			System.out.println("DBに接続しました");
			//自動コミットOFF
			conn.setAutoCommit(false);
		} catch (SQLException | ClassNotFoundException e) {//コネクションの確立に失敗
			System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
			System.out.println("DBとの接続に失敗しました");
			e.printStackTrace();
		}
	}

	//コネクションを配布する
	public Connection getConnection() {
		return conn;
	}

	//コネクションの破棄
	public void closeConnection() {
		try {
			conn.close();
			System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
			System.out.println("DBから切断しました");
		} catch (SQLException e) {
			// TODO 自動生成された catch ブロック
			System.out.print(Thread.currentThread().getStackTrace()[1].getClassName() + ":");
			System.out.println("DBから切断できませんでした");
			e.printStackTrace();
		}
	}
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?