001package springdao.support; 002 003import java.io.Serializable; 004import java.lang.reflect.InvocationTargetException; 005import java.lang.reflect.Method; 006import java.util.ArrayList; 007import java.util.Collection; 008import java.util.List; 009import java.util.Map; 010import java.util.concurrent.atomic.AtomicBoolean; 011import javax.persistence.EntityManager; 012import javax.persistence.EntityManagerFactory; 013import javax.persistence.LockModeType; 014import javax.persistence.PersistenceContext; 015import javax.persistence.PersistenceException; 016import javax.persistence.PersistenceUnit; 017import javax.persistence.PersistenceUnitUtil; 018import javax.persistence.Query; 019import org.apache.logging.log4j.Logger; 020import org.apache.logging.log4j.LogManager; 021import org.springframework.dao.support.DaoSupport; 022import org.springframework.orm.jpa.EntityManagerFactoryUtils; 023import org.springframework.util.ClassUtils; 024import org.springframework.util.ReflectionUtils; 025import springdao.DaoRepository; 026 027/** 028 * 029 * @author Kent Yeh 030 * @param <E> 031 */ 032public abstract class AbstractSpringDao<E> extends DaoSupport implements DaoRepository<E> { 033 034 private static final Logger log = LogManager.getLogger(AbstractSpringDao.class); 035 private EntityManagerFactory emf; 036 private EntityManager em; 037 038 @Override 039 protected void checkDaoConfig() throws IllegalArgumentException { 040 if (this.emf == null) { 041 throw new IllegalArgumentException("'EntityManagerFactory' is required"); 042 } 043 if (this.em == null) { 044 throw new IllegalArgumentException("'EntityManager' is required"); 045 } 046 } 047 048 public EntityManagerFactory getEntityManagerFactory() { 049 return emf; 050 } 051 052 @PersistenceUnit 053 public void setEntityManagerFactory(EntityManagerFactory emf) { 054 this.emf = emf; 055 } 056 057 public EntityManager getEntityManager() { 058 return em; 059 } 060 061 @PersistenceContext 062 public void setEntityManager(EntityManager em) { 063 this.em = em; 064 } 065 066 LockModeType getLockMode(String lockMode) { 067 try { 068 return LockModeType.valueOf(lockMode); 069 } catch (RuntimeException ex) { 070 return LockModeType.NONE; 071 } 072 } 073 074 @Override 075 public E instanate() throws InstantiationException, IllegalAccessException { 076 try { 077 return getClazz().newInstance(); 078 } catch (InstantiationException | IllegalAccessException ex) { 079 log.error("Instanate error", ex); 080 throw ex; 081 } 082 } 083 084 protected RuntimeException convertException(Exception e) { 085 if (e instanceof RuntimeException) { 086 RuntimeException res = EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible((RuntimeException) e); 087 return res == null ? (RuntimeException) e : res; 088 } else { 089 return new RuntimeException(e.getMessage(), e); 090 } 091 } 092 093 @Override 094 public void clear() { 095 try { 096 em.clear(); 097 } catch (RuntimeException e) { 098 throw convertException(e); 099 } 100 } 101 102 @Override 103 public boolean contains(Object entity) { 104 try { 105 return em.contains(entity); 106 } catch (RuntimeException e) { 107 throw convertException(e); 108 } 109 } 110 111 @Override 112 public E findByPrimaryKey(Serializable primaryKey) { 113 try { 114 return em.find(getClazz(), primaryKey); 115 } catch (RuntimeException e) { 116 throw convertException(e); 117 } 118 } 119 120 @Override 121 public E findByPrimaryKey(Serializable primaryKey, String lockMode) { 122 try { 123 return em.find(getClazz(), primaryKey, getLockMode(lockMode)); 124 } catch (RuntimeException e) { 125 throw convertException(e); 126 } 127 } 128 129 @Override 130 public E findByPrimaryKey(Serializable primaryKey, Map<String, Object> properties) { 131 try { 132 return em.find(getClazz(), primaryKey, properties); 133 } catch (RuntimeException e) { 134 throw convertException(e); 135 } 136 } 137 138 @Override 139 public E findByPrimaryKey(Serializable primaryKey, String lockMode, Map<String, Object> properties) { 140 try { 141 return em.find(getClazz(), primaryKey, getLockMode(lockMode), properties); 142 } catch (RuntimeException e) { 143 throw convertException(e); 144 } 145 } 146 147 @Override 148 public E save(E entity) { 149 return persist(entity); 150 } 151 152 @Override 153 public Collection<E> save(Collection<E> entities) { 154 try { 155 ArrayList<E> result = new ArrayList<>(entities.size()); 156 for (E entity : entities) { 157 result.add(persist(entity)); 158 } 159 em.flush(); 160 return result; 161 } catch (RuntimeException e) { 162 throw convertException(e); 163 } 164 } 165 166 @Override 167 public E persist(E entity) { 168 try { 169 em.persist(entity); 170 em.flush(); 171 return entity; 172 } catch (RuntimeException e) { 173 throw convertException(e); 174 } 175 } 176 177 @Override 178 public E update(E entity) { 179 return merge(entity); 180 } 181 182 @Override 183 public Collection<E> update(Collection<E> entities) { 184 return merge(entities); 185 } 186 187 @Override 188 public E merge(E entity) { 189 try { 190 E result = em.merge(entity); 191 em.flush(); 192 return result; 193 } catch (RuntimeException e) { 194 throw convertException(e); 195 } 196 } 197 198 @Override 199 public Collection<E> merge(Collection<E> entities) { 200 try { 201 ArrayList<E> result = new ArrayList<>(entities.size()); 202 for (E entity : entities) { 203 result.add(em.merge(entity)); 204 } 205 em.flush(); 206 return result; 207 } catch (RuntimeException e) { 208 throw convertException(e); 209 } 210 } 211 212 /** 213 * Before using this method, look at 214 * <a 215 * href="http://blog.xebia.com/2009/03/23/jpa-implementation-patterns-saving-detached-entities/">saveOrUpdate 216 * vs. merge</a>. 217 * 218 * @param entity 219 * @return merged entity 220 */ 221 @Override 222 public E saveOrUpdate(E entity) { 223 return contains(entity) ? merge(entity) : emf.getPersistenceUnitUtil().getIdentifier(entity) == null ? persist(entity) : merge(entity); 224 } 225 226 @Override 227 public Collection<E> saveOrUpdate(Collection<E> entities) { 228 try { 229 ArrayList<E> result = new ArrayList<>(entities.size()); 230 for (E entity : entities) { 231 result.add(saveOrUpdate(entity)); 232 } 233 em.flush(); 234 return result; 235 } catch (RuntimeException e) { 236 throw convertException(e); 237 } 238 } 239 240 @Override 241 public void delete(Serializable primaryKey) { 242 try { 243 Object entity = em.find(getClazz(), primaryKey); 244 if (entity != null) { 245 em.remove(entity); 246 em.flush(); 247 } 248 } catch (RuntimeException e) { 249 throw convertException(e); 250 } 251 } 252 253 @Override 254 public void delete(Serializable primaryKey, String lockMode) { 255 try { 256 Object entity = em.find(getClazz(), primaryKey,getLockMode(lockMode)); 257 if (entity != null) { 258 em.remove(entity); 259 em.flush(); 260 } 261 } catch (RuntimeException e) { 262 throw convertException(e); 263 } 264 } 265 266 @Override 267 public void delete(Collection<? extends Serializable> primaryKeys) { 268 try { 269 for (Serializable pk : primaryKeys) { 270 Object entity = em.find(getClazz(), pk); 271 if (entity != null) { 272 em.remove(entity); 273 } 274 } 275 em.flush(); 276 } catch (RuntimeException e) { 277 throw convertException(e); 278 } 279 } 280 281 @Override 282 public E remove(E entity) { 283 entity = merge(entity); 284 try { 285 em.remove(entity); 286 em.flush(); 287 return entity; 288 } catch (RuntimeException e) { 289 throw convertException(e); 290 } 291 } 292 293 @Override 294 public E remove(E entity, String lockMode) { 295 Object pk = emf.getPersistenceUnitUtil().getIdentifier(entity); 296 if (pk == null) { 297 return null; 298 } else { 299 entity = em.find(getClazz(), pk, getLockMode(lockMode)); 300 if (entity == null) { 301 return null; 302 } else { 303 em.remove(entity); 304 em.flush(); 305 return entity; 306 } 307 } 308 } 309 310 @Override 311 public Collection<E> remove(Collection<E> entities) { 312 try { 313 ArrayList<E> result = new ArrayList<>(entities.size()); 314 for (E entity : entities) { 315 entity = merge(entity); 316 em.remove(entity); 317 result.add(entity); 318 } 319 em.flush(); 320 return result; 321 } catch (RuntimeException e) { 322 throw convertException(e); 323 } 324 } 325 326 @Override 327 public E lock(E entity, String lockMode) { 328 try { 329 em.lock(entity, getLockMode(lockMode)); 330 return entity; 331 } catch (RuntimeException e) { 332 throw convertException(e); 333 } 334 } 335 336 @Override 337 public E refresh(E entity) { 338 try { 339 if(em.contains(entity)) 340 em.refresh(entity); 341 else{ 342 Object pk = emf.getPersistenceUnitUtil().getIdentifier(entity); 343 return em.find(getClazz(), pk); 344 } 345 return entity; 346 } catch (RuntimeException e) { 347 throw convertException(e); 348 } 349 } 350 351 @Override 352 public E refresh(E entity, String lockMode) { 353 try { 354 if (em.contains(entity)) { 355 em.refresh(entity, getLockMode(lockMode)); 356 } else { 357 Object pk = emf.getPersistenceUnitUtil().getIdentifier(entity); 358 return em.find(getClazz(), pk, getLockMode(lockMode)); 359 } 360 return entity; 361 } catch (RuntimeException e) { 362 throw convertException(e); 363 } 364 } 365 366 @Override 367 public int sqlUpdate(String sql) { 368 try { 369 return em.createNativeQuery(sql).executeUpdate(); 370 } catch (RuntimeException e) { 371 throw convertException(e); 372 } 373 } 374 375 @Override 376 public int sqlUpdate(String sql, Object... parameters) { 377 try { 378 int i = 0; 379 Query query = em.createNativeQuery(sql); 380 if (parameters != null && parameters.length > 0) { 381 for (Object paramter : parameters) { 382 query.setParameter(++i, paramter); 383 } 384 } 385 return query.executeUpdate(); 386 } catch (RuntimeException e) { 387 throw convertException(e); 388 } 389 } 390 391 @Override 392 public int sqlUpdate(String sql, Map<String, ?> parameters) { 393 try { 394 int i = 0; 395 Query query = em.createNativeQuery(sql); 396 if (parameters != null && !parameters.isEmpty()) { 397 for(Map.Entry<String,?> entry:parameters.entrySet()){ 398 query.setParameter(entry.getKey(), entry.getValue()); 399 } 400 } 401 return query.executeUpdate(); 402 } catch (RuntimeException e) { 403 throw convertException(e); 404 } 405 } 406 407 @Override 408 public List<Integer> sqlUpdate(List<String> sqls) { 409 try { 410 List<Integer> res = new ArrayList<>(sqls.size()); 411 for (String sql : sqls){ 412 res.add(em.createNativeQuery(sql).executeUpdate()); 413 } 414 return res; 415 } catch (RuntimeException e) { 416 throw convertException(e); 417 } 418 } 419 420 @Override 421 public int bulkUpdate(String QL) { 422 try { 423 return em.createQuery(QL).executeUpdate(); 424 } catch (RuntimeException e) { 425 throw convertException(e); 426 } 427 } 428 429 @Override 430 public List<Integer> bulkUpdate(List<String> QLs) { 431 try { 432 List<Integer> result = new ArrayList<>(); 433 for (String ql : QLs) { 434 result.add(em.createQuery(ql).executeUpdate()); 435 } 436 return result; 437 } catch (RuntimeException e) { 438 throw convertException(e); 439 } 440 } 441 442 @Override 443 public int bulkUpdate(String QL, Object... parameters) { 444 try { 445 Query query = em.createQuery(QL); 446 int i = 0; 447 if (parameters != null && parameters.length > 0) { 448 for (Object paramter : parameters) { 449 query.setParameter(++i, paramter); 450 } 451 } 452 return query.executeUpdate(); 453 } catch (RuntimeException e) { 454 throw convertException(e); 455 } 456 } 457 458 @Override 459 public int bulkUpdate(String QL, Map<String, ?> parameters) { 460 try { 461 Query query = em.createQuery(QL); 462 int i = 0; 463 if (parameters != null && !parameters.isEmpty()) { 464 for(Map.Entry<String,?> entry:parameters.entrySet()){ 465 query.setParameter(entry.getKey(), entry.getValue()); 466 } 467 } 468 return query.executeUpdate(); 469 } catch (RuntimeException e) { 470 throw convertException(e); 471 } 472 } 473 474 @Override 475 public String $e() { 476 return getEntityName(); 477 } 478 479 @Override 480 public String getEntityName() { 481 return getClazz().getName(); 482 } 483 484 @Override 485 public String $a() { 486 return getAliasName(); 487 } 488 489 @Override 490 public String $ea() { 491 return getEntityName() + " AS " + getAliasName(); 492 } 493 494 @Override 495 public String getAliasName() { 496 return AliasHelper.$a(getClazz()); 497 } 498 499 protected List<E> findList(Query query) { 500 try { 501 EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory()); 502 return query.getResultList(); 503 } catch (RuntimeException e) { 504 throw convertException(e); 505 } 506 } 507 508 protected List<E> findList(Query query, Object... parameters) { 509 try { 510 EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory()); 511 if (parameters != null && parameters.length > 0) { 512 for (int i = 0; i < parameters.length; i++) { 513 query.setParameter(i + 1, parameters[i]); 514 } 515 } 516 return query.getResultList(); 517 } catch (RuntimeException e) { 518 throw convertException(e); 519 } 520 } 521 522 protected List<E> findList(Query query, Map<String, ?> parameters) { 523 try { 524 EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory()); 525 if (parameters != null && !parameters.isEmpty()) { 526 for(Map.Entry<String,?> entry:parameters.entrySet()){ 527 query.setParameter(entry.getKey(), entry.getValue()); 528 } 529 } 530 return query.getResultList(); 531 } catch (RuntimeException e) { 532 throw convertException(e); 533 } 534 } 535 536 @Override 537 public List<E> findByCriteria(String qlCriteria) { 538 return findList(em.createQuery(JpqlHelper.get().select($a()). 539 from($ea()).$(qlCriteria).ql())); 540 } 541 542 @Override 543 public List<E> findByCriteria(String qlCriteria, Object... parameters) { 544 return findList(em.createQuery(JpqlHelper.get().select($a()). 545 from($ea()).$(qlCriteria).ql()), parameters); 546 } 547 548 @Override 549 public List<E> findByCriteria(String qlCriteria, Map<String, ?> parameters) { 550 return findList(em.createQuery(JpqlHelper.get().select($a()).from($ea()) 551 .$(qlCriteria).ql()), parameters); 552 } 553 554 @Override 555 public List<E> findByCriteria(String qlCriteria, int startPageNo, int pageSize, Object... parameters) { 556 if ((startPageNo < 1) || (pageSize < 1)) { 557 return parameters == null || parameters.length == 0 ? findByCriteria(qlCriteria) : findByCriteria(qlCriteria, parameters); 558 } else if (parameters == null || parameters.length == 0) { 559 return findByCriteria(qlCriteria, startPageNo, pageSize); 560 } else { 561 return findList(em.createQuery(JpqlHelper.get().select($a()).from($ea()).$(qlCriteria).ql()). 562 setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize), parameters); 563 } 564 } 565 566 @Override 567 public List<E> findByCriteria(String qlCriteria, int startPageNo, int pageSize, Map<String, ?> parameters) { 568 if ((startPageNo < 1) || (pageSize < 1)) { 569 return parameters == null || parameters.isEmpty() ? findByCriteria(qlCriteria) : findByCriteria(qlCriteria, parameters); 570 } else if (parameters == null || parameters.isEmpty()) { 571 return findByCriteria(qlCriteria, startPageNo, pageSize); 572 } else { 573 return findList(em.createQuery(JpqlHelper.get().select($a()).from($ea()).$(qlCriteria).ql()) 574 .setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize), parameters); 575 } 576 } 577 578 @Override 579 public List<E> findByCriteria(String qlCriteria, int startPageNo, int pageSize) { 580 if ((startPageNo < 1) || (pageSize < 1)) { 581 return findByCriteria(qlCriteria); 582 } else { 583 return findList(em.createQuery(JpqlHelper.get().select($a()).from($ea()).$(qlCriteria).ql()).setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize)); 584 } 585 } 586 587 @Override 588 public List<E> findBySQLQuery(String sql) { 589 return findList(em.createNativeQuery(sql, getClazz())); 590 } 591 592 @Override 593 public List<E> findBySQLQuery(String sql, Object... parameters) { 594 return findList(em.createNativeQuery(sql, getClazz()), parameters); 595 } 596 597 @Override 598 public List<E> findBySQLQuery(String sql, Map<String, ?> parameters) { 599 return findList(em.createNativeQuery(sql, getClazz()), parameters); 600 } 601 602 @Override 603 public <T> T findUniqueByQL(String QL) { 604 try { 605 Query query = em.createQuery(QL); 606 EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory()); 607 return (T) query.getSingleResult(); 608 } catch (RuntimeException e) { 609 throw convertException(e); 610 } 611 } 612 613 @Override 614 public <T> T findUniqueByQL(String QL, Object... parameters) { 615 try { 616 Query query = em.createQuery(QL); 617 EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory()); 618 if (parameters != null && parameters.length > 0) { 619 for (int i = 0; i < parameters.length; i++) { 620 query.setParameter(i + 1, parameters[i]); 621 } 622 } 623 return (T) query.getSingleResult(); 624 } catch (RuntimeException e) { 625 throw convertException(e); 626 } 627 } 628 629 @Override 630 public <T> T findUniqueByQL(String QL, Map<String, ?> parameters) { 631 try { 632 Query query = em.createQuery(QL); 633 EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory()); 634 if (parameters != null && !parameters.isEmpty()) { 635 for(Map.Entry<String,?> entry:parameters.entrySet()){ 636 query.setParameter(entry.getKey(), entry.getValue()); 637 } 638 } 639 return (T) query.getSingleResult(); 640 } catch (RuntimeException e) { 641 throw convertException(e); 642 } 643 } 644 645 @Override 646 public <T> List<T> findListByQL(String QL) { 647 try { 648 Query query = em.createQuery(QL); 649 EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory()); 650 return query.getResultList(); 651 } catch (RuntimeException e) { 652 log.error(e.getMessage(), e); 653 throw convertException(e); 654 } 655 } 656 657 @Override 658 public <T> List<T> findListByQL(String QL, Object... parameters) { 659 try { 660 Query query = em.createQuery(QL); 661 EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory()); 662 if (parameters != null && parameters.length > 0) { 663 for (int i = 0; i < parameters.length; i++) { 664 query.setParameter(i + 1, parameters[i]); 665 } 666 } 667 return query.getResultList(); 668 } catch (RuntimeException e) { 669 throw convertException(e); 670 } 671 } 672 673 @Override 674 public <T> List<T> findListByQL(String QL, Map<String, ?> parameters) { 675 try { 676 Query query = em.createQuery(QL); 677 EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory()); 678 if (parameters != null && !parameters.isEmpty()) { 679 for(Map.Entry<String,?> entry:parameters.entrySet()){ 680 query.setParameter(entry.getKey(), entry.getValue()); 681 } 682 } 683 return query.getResultList(); 684 } catch (RuntimeException e) { 685 throw convertException(e); 686 } 687 } 688 689 @Override 690 public List<E> findByNamedQuery(String name) { 691 return findList(em.createNamedQuery(name, getClazz())); 692 } 693 694 @Override 695 public List<E> findByNamedQuery(String name, Object... parameters) { 696 return findList(em.createNamedQuery(name, getClazz()), parameters); 697 } 698 699 @Override 700 public List<E> findByNamedQuery(String name, Map<String, ?> parameters) { 701 return findList(em.createNamedQuery(name, getClazz()), parameters); 702 } 703 704 @Override 705 public <T> List<T> findListByNamedQuery(String name) { 706 try { 707 Query query = em.createNamedQuery(name); 708 EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory()); 709 return query.getResultList(); 710 } catch (RuntimeException e) { 711 throw convertException(e); 712 } 713 } 714 715 @Override 716 public <T> List<T> findListByNamedQuery(String name, Object... parameters) { 717 try { 718 Query query = em.createNamedQuery(name); 719 EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory()); 720 if (parameters != null && parameters.length > 0) { 721 for (int i = 0; i < parameters.length; i++) { 722 query.setParameter(i + 1, parameters[i]); 723 } 724 } 725 return query.getResultList(); 726 } catch (RuntimeException e) { 727 throw convertException(e); 728 } 729 } 730 731 @Override 732 public <T> List<T> findListByNamedQuery(String name, Map<String, ?> parameters) { 733 try { 734 Query query = em.createNamedQuery(name); 735 EntityManagerFactoryUtils.applyTransactionTimeout(query, getEntityManagerFactory()); 736 if (parameters != null && !parameters.isEmpty()) { 737 for(Map.Entry<String,?> entry:parameters.entrySet()){ 738 query.setParameter(entry.getKey(), entry.getValue()); 739 } 740 } 741 return query.getResultList(); 742 } catch (RuntimeException e) { 743 throw convertException(e); 744 } 745 } 746 747 @Override 748 public E initLazyCollection(E entity, final String collectionFieldName) { 749 final AtomicBoolean found = new AtomicBoolean(false); 750 final String methodName = collectionFieldName.matches("^[a-z][A-Z]") ? collectionFieldName : collectionFieldName.length() > 1 751 ? collectionFieldName.substring(0, 1).toUpperCase() + collectionFieldName.substring(1) : collectionFieldName.toUpperCase(); 752 final Object obj = entity; 753 final EntityManager fem = em; 754 try { 755 ReflectionUtils.doWithMethods(getClazz(), 756 new ReflectionUtils.MethodCallback() { 757 @Override 758 public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { 759 try { 760 Method setter = obj.getClass().getMethod("s" + method.getName().substring(1), method.getReturnType()); 761 PersistenceUnitUtil puu = fem.getEntityManagerFactory().getPersistenceUnitUtil(); 762 if (!puu.isLoaded(obj, collectionFieldName)) { 763 E reattach = (E) fem.merge(obj); 764// E reattach = (E) em.find(obj.getClass(), puu.getIdentifier(obj)); 765 Object fieldObj = method.invoke(reattach, new Object[]{}); 766 ((Collection) fieldObj).size(); 767 setter.invoke(obj, fieldObj); 768 } 769 } catch (NoSuchMethodException ex) { 770 throw new PersistenceException("Setter " + getClazz().getSimpleName() + ".set" + methodName + "(...) not found.", ex); 771 } catch (InvocationTargetException ex) { 772 throw new PersistenceException("Could not fetch Collection from " + getClazz().getSimpleName() + "." + method.getName(), ex); 773 } 774 } 775 }, 776 new ReflectionUtils.MethodFilter() { 777 @Override 778 public boolean matches(Method method) { 779 if (found.get()) { 780 return false; 781 } else { 782 found.set(method.getName().equals("get" + methodName) && method.getParameterTypes().length == 0 783 && ClassUtils.isAssignable(Collection.class, method.getReturnType())); 784 return found.get(); 785 } 786 } 787 }); 788 return (E) obj; 789 } catch (IllegalArgumentException e) { 790 throw convertException(e); 791 } finally { 792 EntityManagerFactoryUtils.closeEntityManager(fem); 793 } 794 } 795}