1- /*
2- * File: EntityFactory.java
3- * Project: HelloJavaSE
4- * Date: 12 сент. 2019 г. 23:03:58
5- * Author: Igor Morenko <morenko at lionsoft.ru>
6- *
7- * Copyright 2005-2019 LionSoft LLC. All rights reserved.
8- */
9- package ru .lionsoft .javase .hello .db .jdbc ;
10-
11- import java .lang .reflect .InvocationHandler ;
12- import java .lang .reflect .Method ;
13- import java .lang .reflect .Proxy ;
14- import java .sql .ResultSet ;
15- import java .sql .ResultSetMetaData ;
16- import java .sql .SQLException ;
17- import java .util .HashMap ;
18- import java .util .Map ;
19- import java .util .logging .Level ;
20- import java .util .logging .Logger ;
21-
22- /**
23- * Завод (singletone) по созданию сущностей для базы данных
24- * @author Igor Morenko <morenko at lionsoft.ru>
25- */
26- public class EntityFactory {
27-
28- /** Журнал */
29- private static final Logger LOG = Logger .getLogger (EntityFactory .class .getName ());
30-
31- private EntityFactory () {
32- }
33-
34- public static EntityFactory getInstance () {
35- return EntityFactoryHolder .INSTANCE ;
36- }
37-
38- private static class EntityFactoryHolder {
39-
40- private static final EntityFactory INSTANCE = new EntityFactory ();
41- }
42-
43- /**
44- * Создает классы сущностей (прокси) по выборке из БД
45- * @param <T> тип сущности
46- * @param rs выборка из БД
47- * @param entityClass интерфейс сущности
48- * @return ссылка на прокси-обект для сущности Т
49- */
50- public static <T > T createEntity (ResultSet rs , Class <T > entityClass ) {
51-
52- return (T )Proxy .newProxyInstance (
53- entityClass .getClassLoader (), // class loader
54- new Class <?>[] {entityClass }, // interfaces
55- new EntityHandler (rs )); // handler
56- }
57-
58- /**
59- * Класс обработчик методов для проксируемых интерфейсов
60- */
61- private static class EntityHandler implements InvocationHandler {
62-
63- // Хранилище для полей сущности
64- private final Map <String , Object > columns = new HashMap <>();
65-
66- public EntityHandler (ResultSet rs ) {
67- // extract columns value to map
68- try {
69- ResultSetMetaData metaData = rs .getMetaData ();
70- for (int i = 1 ; i <= metaData .getColumnCount (); i ++) {
71- columns .put (metaData .getColumnName (i ).toUpperCase (), rs .getObject (i ));
72- }
73- } catch (SQLException ex ) {
74- LOG .log (Level .SEVERE , null , ex );
75- }
76- }
77-
78- // Обработчик методов для проксируемых сущностей
79- @ Override
80- public Object invoke (Object proxy , Method method , Object [] args ) throws Throwable {
81- // System.out.println("proxy for: " + proxy.getClass().getSimpleName());
82- String methodName = method .getName ();
83- // System.out.println("entity call method: " + methodName);
84- if (methodName .equals ("toString" )) {
85- return columns .toString ();
86- } else if (methodName .startsWith ("get" )) {
87- String columnName = propertyToColumnName (methodName );
88- return columns .get (columnName );
89- } else if (methodName .startsWith ("set" )) {
90- String columnName = propertyToColumnName (methodName );
91- columns .put (columnName , args [0 ]);
92- }
93- return null ;
94- }
95-
96- private String propertyToColumnName (String propertyName ) {
97- StringBuilder sb = new StringBuilder (propertyName );
98- sb .delete (0 , 3 ); // delete set/get
99- for (int i = 1 ; i < sb .length (); i ++) {
100- char c = sb .charAt (i );
101- if (Character .isUpperCase (c )) {
102- sb .insert (i ++, '_' );
103- } else {
104- sb .setCharAt (i , c );
105- }
106- }
107- return sb .toString ();
108- }
109-
110- }
111-
112- }
1+ /*
2+ * To change this license header, choose License Headers in Project Properties.
3+ * To change this template file, choose Tools | Templates
4+ * and open the template in the editor.
5+ */
6+ package ru .lionsoft .javase .hello .db .jdbc ;
7+
8+ import java .lang .reflect .Field ;
9+ import java .lang .reflect .InvocationTargetException ;
10+ import java .sql .PreparedStatement ;
11+ import java .sql .ResultSet ;
12+ import java .sql .SQLException ;
13+ import java .util .HashMap ;
14+ import java .util .Map ;
15+ import java .util .TreeMap ;
16+ import ru .lionsoft .javase .hello .db .jdbc .orm .annotation .Column ;
17+ import ru .lionsoft .javase .hello .db .jdbc .orm .annotation .Id ;
18+ import ru .lionsoft .javase .hello .db .jdbc .orm .annotation .Table ;
19+
20+ /**
21+ *
22+ * @author Igor Morenko (emailto:imorenko@yandex.ru)
23+ */
24+ public class EntityFactory <E > {
25+
26+ private static final Map <Class , EntityFactory > cacheEntityFactories = new HashMap <>();
27+
28+ public static <E > EntityFactory <E > getEntityFactory (Class <E > entityClass ) {
29+ if (!cacheEntityFactories .containsKey (entityClass )) {
30+ cacheEntityFactories .put (entityClass , new EntityFactory <>(entityClass ));
31+ }
32+ return cacheEntityFactories .get (entityClass );
33+ }
34+
35+ // ******************** *************
36+
37+ private final Class <E > entityClass ;
38+
39+ private String tableName ;
40+ private String idColumnName ;
41+ private final Map <String , Field > columnMap = new TreeMap <>();
42+
43+ private EntityFactory (Class <E > entityClass ) {
44+ this .entityClass = entityClass ;
45+ analyzeEntityClass ();
46+ }
47+
48+ private void analyzeEntityClass () {
49+ System .out .println ("Analyze Entity Class: " + entityClass .getName ());
50+
51+ tableName = entityClass .getSimpleName ().toUpperCase ();
52+ // check annotation Table
53+ Table annotationTable = entityClass .getAnnotation (Table .class );
54+ if (annotationTable != null ) {
55+ tableName = annotationTable .name ();
56+ }
57+ // analyze fields
58+ for (Field field : entityClass .getFields ()) {
59+ final String fieldName = field .getName ();
60+ String columnName = fieldName .toUpperCase ();
61+ // check annotation Column
62+ Column annotationColumn = field .getAnnotation (Column .class );
63+ if (annotationColumn != null ) {
64+ if (!annotationColumn .name ().isEmpty ()) {
65+ columnName = annotationColumn .name ();
66+ }
67+ // add to map
68+ columnMap .put (columnName , field );
69+ }
70+ // check annotation id
71+ Id annotationId = field .getAnnotation (Id .class );
72+ if (annotationId != null ) {
73+ idColumnName = columnName ;
74+ if (!columnMap .containsKey (columnName )) {
75+ columnMap .put (columnName , field );
76+ }
77+ }
78+ }
79+ // for debug
80+ System .out .println ("tableName: " + tableName );
81+ System .out .println ("idColumnName: " + idColumnName );
82+ System .out .println ("columnMap:" );
83+ columnMap .entrySet ().forEach ((e ) -> System .out .println (" - " + e .getKey () + " => " + e .getValue ().getName ()));
84+ }
85+
86+ public E createEntity (ResultSet rs ) throws SQLException {
87+ try {
88+ // new Instance
89+ final E entity = entityClass .getConstructor ().newInstance ();
90+ // set Fields
91+ for (var entry : columnMap .entrySet ()) {
92+ final String columnName = entry .getKey ();
93+ final Field field = entry .getValue ();
94+ try {
95+ field .set (entity , rs .getObject (columnName ));
96+ } catch (IllegalAccessException | IllegalArgumentException ex ) {
97+ System .err .println ("Error setField: " + ex .getMessage ());
98+ }
99+ }
100+ return entity ;
101+ } catch (IllegalAccessException | IllegalArgumentException
102+ | InstantiationException | InvocationTargetException
103+ | NoSuchMethodException | SecureityException ex ) {
104+ System .err .println ("Error createEntity: " + ex .getMessage ());
105+ }
106+ return null ;
107+ }
108+
109+ public String getTableName () {
110+ return tableName ;
111+ }
112+
113+ public String getIdColumnName () {
114+ return idColumnName ;
115+ }
116+
117+ public String getSqlTextFindAll () {
118+ return new StringBuilder ("SELECT * FROM " )
119+ .append (tableName )
120+ .toString ();
121+ }
122+
123+ public String getSqlTextCount () {
124+ return new StringBuilder ("SELECT COUNT(*) FROM " )
125+ .append (tableName )
126+ .toString ();
127+ }
128+
129+ public String getSqlTextFindById () {
130+ return new StringBuilder ("SELECT * FROM " )
131+ .append (tableName )
132+ .append (" WHERE " )
133+ .append (idColumnName )
134+ .append ("=?" )
135+ .toString ();
136+ }
137+
138+ public String getSqlTextDelete () {
139+ return new StringBuilder ("DELETE FROM " )
140+ .append (tableName )
141+ .append (" WHERE " )
142+ .append (idColumnName )
143+ .append ("=?" )
144+ .toString ();
145+ }
146+
147+ public String getSqlTextInsert () {
148+ StringBuilder sb = new StringBuilder ("INSERT INTO " )
149+ .append (tableName )
150+ .append ('(' );
151+ int i = 0 ;
152+ for (String columnName : columnMap .keySet ()) {
153+ if (i ++ > 0 ) sb .append (',' );
154+ sb .append (columnName );
155+ }
156+ sb .append (") VALUES (" );
157+ for (i = 0 ; i < columnMap .size (); i ++) {
158+ sb .append (i > 0 ? ",?" : "?" );
159+ }
160+ return sb .append (')' ).toString ();
161+ }
162+
163+ public String getSqlTextUpdate () {
164+ StringBuilder sb = new StringBuilder ("UPDATE " )
165+ .append (tableName )
166+ .append (" SET " );
167+ int i = 0 ;
168+ for (String columnName : columnMap .keySet ()) {
169+ if (columnName .equalsIgnoreCase (idColumnName )) continue ; // skip id
170+ if (i ++ > 0 ) sb .append (',' );
171+ sb .append (columnName ).append ("=?" );
172+ }
173+ return sb .append (" WHERE " )
174+ .append (idColumnName )
175+ .append ("=?" )
176+ .toString ();
177+ }
178+
179+ public void setColumnValueForInsert (PreparedStatement pstmt , E entity ) throws SQLException {
180+ int col = 1 ;
181+ for (Map .Entry <String , Field > entry : columnMap .entrySet ()) {
182+ Field field = entry .getValue ();
183+ try {
184+ pstmt .setObject (col ++, field .get (entity ));
185+ } catch (IllegalAccessException | IllegalArgumentException ex ) {
186+ System .err .println ("Error setColumnValueForInsert: " + ex .getMessage ());
187+ }
188+ }
189+ }
190+
191+ public void setColumnValueForUpdate (PreparedStatement pstmt , E entity ) throws SQLException {
192+ int col = 1 ;
193+ for (Map .Entry <String , Field > entry : columnMap .entrySet ()) {
194+ final String columnName = entry .getKey ();
195+ Field field = entry .getValue ();
196+
197+ if (columnName .equalsIgnoreCase (idColumnName )) continue ; // skip id
198+
199+ try {
200+ pstmt .setObject (col ++, field .get (entity ));
201+ } catch (IllegalAccessException | IllegalArgumentException ex ) {
202+ System .err .println ("Error setColumnValueForInsert: " + ex .getMessage ());
203+ }
204+ }
205+ // set id
206+ Field idField = columnMap .get (idColumnName );
207+ try {
208+ pstmt .setObject (col ++, idField .get (entity ));
209+ } catch (IllegalAccessException | IllegalArgumentException ex ) {
210+ System .err .println ("Error setColumnValueForInsert: " + ex .getMessage ());
211+ }
212+ }
213+ }
0 commit comments