001/* 002 * JKNIV, whinstone one contract to access your database. 003 * 004 * Copyright (C) 2017, the original author or authors. 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software Foundation, Inc., 018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 019 */ 020package net.sf.jkniv.whinstone.jpa2; 021 022import java.sql.ResultSet; 023import java.util.Map; 024 025import javax.persistence.EntityManager; 026import javax.persistence.Query; 027 028import org.slf4j.Logger; 029 030import net.sf.jkniv.asserts.Assertable; 031import net.sf.jkniv.asserts.AssertsFactory; 032import net.sf.jkniv.exception.HandleableException; 033import net.sf.jkniv.reflect.beans.ObjectProxy; 034import net.sf.jkniv.reflect.beans.ObjectProxyFactory; 035import net.sf.jkniv.sqlegance.LanguageType; 036import net.sf.jkniv.sqlegance.QueryNotFoundException; 037import net.sf.jkniv.sqlegance.Sql; 038import net.sf.jkniv.sqlegance.logger.DataMasking; 039import net.sf.jkniv.whinstone.Queryable; 040import net.sf.jkniv.whinstone.ResultRow; 041import net.sf.jkniv.whinstone.commands.Command; 042import net.sf.jkniv.whinstone.commands.CommandAdapter; 043import net.sf.jkniv.whinstone.jpa2.commands.DefaultJpaCommand; 044import net.sf.jkniv.whinstone.jpa2.commands.DefaultJpaQuery; 045import net.sf.jkniv.whinstone.jpa2.commands.MergeCommand; 046import net.sf.jkniv.whinstone.jpa2.commands.PersistCommand; 047import net.sf.jkniv.whinstone.jpa2.commands.RemoveCommand; 048import net.sf.jkniv.whinstone.jpa2.statement.JpaStatementAdapter; 049import net.sf.jkniv.whinstone.statement.AutoKey; 050import net.sf.jkniv.whinstone.statement.StatementAdapter; 051 052/** 053 * 054 * @author Alisson Gomes 055 * @since 0.6.0 056 */ 057public class JpaCommandAdapter implements CommandAdapter 058{ 059 private static final Logger LOG = LoggerFactory.getLogger(); 060 private static final Logger SQLLOG = net.sf.jkniv.whinstone.jpa2.LoggerFactory.getLogger(); 061 private static final DataMasking MASKING = net.sf.jkniv.whinstone.jpa2.LoggerFactory.getDataMasking(); 062 private static final Assertable NOT_NULL = AssertsFactory.getNotNull(); 063 private static final String ENTITY_ANNOTATION = "javax.persistence.Entity"; 064 private final String contextName; 065 private final HandleableException handlerException; 066 private JpaEmFactory emFactory; 067 068 public JpaCommandAdapter(String contextName, JpaEmFactory emFactory, HandleableException handlerException) 069 { 070 NOT_NULL.verify(contextName, emFactory, handlerException); 071 this.contextName = contextName; 072 this.handlerException = handlerException; 073 this.emFactory = emFactory; 074 } 075 076 @Override 077 public String getContextName() 078 { 079 return this.contextName; 080 } 081 082 @Override 083 public void close() //throws SQLException 084 { 085 } 086 087 @SuppressWarnings({ "unchecked", "rawtypes" }) 088 @Override 089 public <T, R> Command asSelectCommand(Queryable queryable, ResultRow<T, R> overloadResultRow) 090 { 091 Command command = null; 092 String sql = queryable.query(); 093 if (SQLLOG.isInfoEnabled()) 094 SQLLOG.info("Bind Native SQL\n{}", sql); 095 096 Query queryJpa = build(queryable); 097 StatementAdapter<T, R> stmt = new JpaStatementAdapter(queryJpa, queryable, this.handlerException); 098 queryable.bind(stmt).on(); 099 stmt.with(overloadResultRow); 100 command = new DefaultJpaQuery(queryable).with(stmt); 101 return command; 102 } 103 104 @Override 105 public <T, R> Command asUpdateCommand(Queryable queryable) 106 { 107 Command command = null; 108 if (isEntity(queryable)) 109 command = new MergeCommand(getEntityManager(), queryable); 110 else 111 command = buildCommand(queryable); 112 113 return command; 114 } 115 116 @Override 117 public <T, R> Command asRemoveCommand(Queryable queryable)//, ResultRow<T, R> overloadResultRow) 118 { 119 Command command = null; 120 if (isEntity(queryable)) 121 command = new RemoveCommand(queryable).with(getEntityManager()); 122 else 123 command = buildCommand(queryable); 124 125 return command; 126 } 127 128 @Override 129 public <T, R> Command asAddCommand(Queryable queryable) 130 { 131 Command command = null; 132 if (isEntity(queryable)) 133 command = new PersistCommand(queryable).with(getEntityManager()); 134 else 135 command = buildCommand(queryable); 136 137 return command; 138 } 139 140 @SuppressWarnings( 141 { "rawtypes" }) 142 private <T, R> Command buildCommand(Queryable queryable) 143 { 144 Command command = null; 145 String sql = queryable.query(); 146 AutoKey auto = null; 147 if (SQLLOG.isInfoEnabled()) 148 SQLLOG.info("Bind Native SQL\n{}", sql); 149 150 Query queryJpa = build(queryable); 151 152 JpaStatementAdapter<Number, ResultSet> stmt = new JpaStatementAdapter<Number, ResultSet>(queryJpa, queryable, 153 this.handlerException); 154 155 // if (queryable.getDynamicSql().isBatch() || queryable.isTypeOfBulk()) 156 // command = new BulkCommand((CassandraPreparedStatementAdapter) stmt, queryable); 157 // else if (queryable.getDynamicSql().isInsertable() 158 // && queryable.getDynamicSql().asInsertable().isAutoGenerateKey()) 159 // { 160 // Insertable isql = queryable.getDynamicSql().asInsertable(); 161 // // isSequenceStrategy and isAutoStrategy are the the same in cassandra uuid value is generated 162 // // if(isql.getAutoGeneratedKey().isAutoStrategy()) 163 // auto = new CassandraSequenceGeneratedKey(isql, this.session, handlerException); 164 // command = new AddSequenceKeyJdbcCommand(stmt, queryable); 165 // } 166 // else 167 // command = new DefaultCommand((CassandraPreparedStatementAdapter) stmt, queryable); 168 169 command = new DefaultJpaCommand(queryable).with(stmt); 170 stmt.with(auto); 171 return command; 172 } 173 174 private boolean isEntity(Queryable queryable) 175 { 176 boolean isEntity = false; 177 if (queryable.getParams() != null) 178 { 179 isEntity = ObjectProxyFactory.of(queryable.getParams()).mute(ClassNotFoundException.class) 180 .hasAnnotation(ENTITY_ANNOTATION); 181 } 182 return (isEntity && 183 (queryable.getDynamicSql().getLanguageType() == LanguageType.HQL 184 || queryable.getDynamicSql().getLanguageType() == LanguageType.JPQL)); 185 } 186 187// @Override 188// public <T, R> StatementAdapter<T, R> newStatement(String sql, LanguageType languageType) 189// { 190// Query queryJpa = getEntityManager().createQuery(sql); 191// StatementAdapter<T, R> stmt = new JpaStatementAdapter(queryJpa, null, this.handlerException); 192// return stmt; 193// } 194 195 private Query build(Queryable queryable) 196 { 197 Sql sql = queryable.getDynamicSql(); 198 LanguageType languageType = sql.getLanguageType(); 199 Class<?> overloadReturnedType = sql.getReturnTypeAsClass(); 200 EntityManager em = getEntityManager(); 201 Query queryJpa = null; 202 String stringSql = queryable.query(); 203 boolean returnTypeIsEntity = false; 204 if (queryable.getDynamicSql().hasReturnType()) 205 { 206 ObjectProxy<?> proxyReturnType = ObjectProxyFactory.of(queryable.getDynamicSql().getReturnType()); 207 returnTypeIsEntity = proxyReturnType.hasAnnotation(ENTITY_ANNOTATION); 208 } 209 switch (languageType) 210 { 211 case JPQL: 212 if (sql instanceof NamedQueryForSql) 213 { 214 try 215 { 216 if (!Map.class.getName().equals(queryable.getReturnType().getName())) 217 queryJpa = em.createNamedQuery(sql.getName(), sql.getReturnTypeAsClass()); 218 else 219 queryJpa = em.createNamedQuery(sql.getName()); 220 221 this.initParams(queryable, queryJpa); 222 } 223 catch (IllegalArgumentException notFound) 224 { 225 throw new QueryNotFoundException("Named Query not found [" + queryable.getName() + "] check if orm.xml have the query named or it's annotated"); 226 } 227 } 228 else if (returnTypeIsEntity) 229 queryJpa = em.createQuery(stringSql, overloadReturnedType); 230 else 231 queryJpa = em.createQuery(stringSql); 232 233 break; 234 case NATIVE: 235 if (returnTypeIsEntity) 236 queryJpa = em.createNativeQuery(stringSql, overloadReturnedType); 237 else 238 queryJpa = em.createNativeQuery(stringSql); 239 break; 240 case STORED: 241 throw new UnsupportedOperationException( 242 "RepositoryJpa supports JPQL or NATIVE queries none other, Stored Procedure is pending to implements"); 243 // queryJpa = em.createStoredProcedureQuery(isql.asStorable().getSpName()); 244 default: 245 throw new UnsupportedOperationException( 246 "RepositoryJpa supports JPQL or NATIVE queries none other, Stored Procedure is pending to implements"); 247 } 248 249 return queryJpa; 250 /* 251 Class<?> mandatoryReturnType = null; 252 Sql isql = null; 253 boolean containQuery = sqlContext.containsQuery(queryable.getName()); 254 if (containQuery) 255 { 256 isql = sqlContext.getQuery(queryable.getName()); 257 if (overloadReturnedType != null) 258 mandatoryReturnType = overloadReturnedType; 259 else if (isql.getReturnTypeAsClass() != null) 260 mandatoryReturnType = isql.getReturnTypeAsClass(); 261 262 adapter = new QueryJpaAdapter(em, queryable, isql, mandatoryReturnType); 263 if (queryable.isPaging()) 264 adapter.setQueryJpaForPaging(getQueryForPaging(em, sqlContext, queryable, isql)); 265 } 266 else 267 { 268 adapter = new NamedQueryJpaAdapter(em, queryable, overloadReturnedType); 269 } 270 return adapter; 271 */ 272 } 273 274 private void initParams(Queryable queryable, Query queryJpa) 275 { 276 if (!queryable.isTypeOfNull()) 277 { 278 if (queryable.isTypeOfMap()) 279 { 280 //PrepareParams<Query> prepareParams = PrepareParamsFactory.newPrepareParams(queryJpa, new ParamParserColonMark(), queryable); 281 setMapParams(queryJpa, (Map) queryable.getParams()); 282 } 283 else if (queryable.getParams().getClass().isArray()) 284 { 285 //PrepareParams<Query> prepareParams = PrepareParamsFactory.newPrepareParams(queryJpa, new ParamParserQuestionMark(), queryable); 286 setArrayParams(queryJpa, (Object[]) queryable.getParams()); 287 } 288 } 289 if (queryable.isPaging()) 290 queryJpa.setFirstResult(queryable.getOffset()).setMaxResults(queryable.getMax()); 291 } 292 293 private void setMapParams(Query query, Map<String, Object> params) 294 { 295 int i = 0; 296 for (String s : params.keySet()) 297 { 298 Object o = params.get(s); 299 if (SQLLOG.isDebugEnabled())// TODO making data sqlLogger.mask 300 SQLLOG.debug("Setting SQL Parameter from index [{}] with name [{}] with value of [{}] type of [{}]", i++, 301 s, o, (o == null ? "NULL" : o.getClass())); 302 303 query.setParameter(s, o); 304 } 305 } 306 307 private void setArrayParams(Query query, Object[] params) 308 { 309 int i = 1; 310 for (Object o : params) 311 { 312 if (SQLLOG.isDebugEnabled())// TODO making data sqlLogger.mask 313 SQLLOG.debug("Setting SQL Parameter from index [{}] with name [{}] with value of [{}] type of [{}]", i, 314 "?", o, (o == null ? "NULL" : o.getClass())); 315 query.setParameter(i++, o); 316 } 317 } 318 319 private EntityManager getEntityManager() 320 { 321 EntityManager em = emFactory.createEntityManager(); 322 LOG.trace("Lookup Entity Manager " + em); 323 return em; 324 } 325 326 @Override 327 public String toString() 328 { 329 return "JpaCommandAdapter [contextName=" + contextName + ", emFactory=" + emFactory + "]"; 330 } 331 332}