001/** 002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.jboot.db.model; 017 018import com.jfinal.kit.LogKit; 019import com.jfinal.log.Log; 020import com.jfinal.plugin.activerecord.*; 021import com.jfinal.plugin.activerecord.dialect.Dialect; 022import io.jboot.db.JbootDb; 023import io.jboot.db.SqlDebugger; 024import io.jboot.db.dialect.JbootDialect; 025import io.jboot.exception.JbootException; 026import io.jboot.exception.JbootIllegalConfigException; 027import io.jboot.utils.ClassUtil; 028import io.jboot.utils.StrUtil; 029 030import java.sql.Connection; 031import java.sql.PreparedStatement; 032import java.sql.SQLException; 033import java.sql.Statement; 034import java.util.*; 035 036 037/** 038 * @author michael yang 039 */ 040public class JbootModel<M extends JbootModel<M>> extends Model<M> { 041 042 private static final Log LOG = Log.getLog(JbootModel.class); 043 private static final String DATASOURCE_CACHE_PREFIX = "__ds__"; 044 045 private static JbootModelConfig config = JbootModelConfig.getConfig(); 046 private static String column_created = config.getColumnCreated(); 047 private static String column_modified = config.getColumnModified(); 048 private static boolean idCacheEnable = config.isIdCacheEnable(); 049 050 protected List<Join> joins = null; 051 String datasourceName = null; 052 String alias = null; 053 String loadColumns = null; 054 boolean isCopyModel = false; 055 056 057 public Joiner<M> leftJoin(String table) { 058 return joining(Join.TYPE_LEFT, table, true); 059 } 060 061 public Joiner<M> leftJoinIf(String table, boolean condition) { 062 return joining(Join.TYPE_LEFT, table, condition); 063 } 064 065 public Joiner<M> rightJoin(String table) { 066 return joining(Join.TYPE_RIGHT, table, true); 067 } 068 069 public Joiner<M> rightJoinIf(String table, boolean condition) { 070 return joining(Join.TYPE_RIGHT, table, condition); 071 } 072 073 public Joiner<M> innerJoin(String table) { 074 return joining(Join.TYPE_INNER, table, true); 075 } 076 077 public Joiner<M> innerJoinIf(String table, boolean condition) { 078 return joining(Join.TYPE_INNER, table, condition); 079 } 080 081 public Joiner<M> fullJoin(String table) { 082 return joining(Join.TYPE_FULL, table, true); 083 } 084 085 public Joiner<M> fullJoinIf(String table, boolean condition) { 086 return joining(Join.TYPE_FULL, table, condition); 087 } 088 089 /** 090 * set table alias in sql 091 * 092 * @param alias 093 * @return 094 */ 095 public M alias(String alias) { 096 if (StrUtil.isBlank(alias)) { 097 throw new IllegalArgumentException("alias must not be null or empty."); 098 } 099 M model = getOrCopyDao(); 100 model.alias = alias; 101 return model; 102 } 103 104 105 protected Joiner<M> joining(String type, String table, boolean condition) { 106 M model = getOrCopyDao(); 107 if (model.joins == null) { 108 model.joins = new LinkedList<>(); 109 } 110 Join join = new Join(type, table, condition); 111 model.joins.add(join); 112 return new Joiner<>(model, join); 113 } 114 115 116 /** 117 * set load columns in sql 118 * 119 * @param loadColumns 120 * @return 121 */ 122 public M loadColumns(String loadColumns) { 123 if (StrUtil.isBlank(loadColumns)) { 124 throw new IllegalArgumentException("loadColumns must not be null or empty."); 125 } 126 M model = getOrCopyDao(); 127 model.loadColumns = loadColumns; 128 return model; 129 } 130 131 132 public M distinct(String columnName) { 133 if (StrUtil.isBlank(columnName)) { 134 throw new IllegalArgumentException("columnName must not be null or empty."); 135 } 136 M dao = getOrCopyDao(); 137 JbootModelExts.setDistinctColumn(dao, columnName); 138 return dao; 139 } 140 141 142 private M getOrCopyDao() { 143 if (isCopyModel) { 144 return (M) this; 145 } else { 146 M dao = copy()._setConfigName(datasourceName); 147 dao.isCopyModel = true; 148 return dao; 149 } 150 } 151 152 @Override 153 public M dao() { 154 put("__is_dao", true); 155 return (M) this; 156 } 157 158 159 private boolean isDaoModel() { 160 Boolean flag = getBoolean("__is_dao"); 161 return flag != null && flag; 162 } 163 164 /** 165 * copy model with attrs or false 166 * 167 * @return 168 */ 169 public M copy() { 170 M m = null; 171 try { 172 m = (M) _getUsefulClass().newInstance(); 173 m.put(_getAttrs()); 174 175 for (String attr : _getModifyFlag()) { 176 m._getModifyFlag().add(attr); 177 } 178 } catch (Exception e) { 179 LOG.error(e.toString(), e); 180 } 181 return m; 182 } 183 184 /** 185 * copy new model with db attrs and fill modifyFlag 186 * 187 * @return 188 */ 189 public M copyModel() { 190 M m = null; 191 try { 192 m = (M) _getUsefulClass().newInstance(); 193 Table table = _getTable(true); 194 Set<String> attrKeys = table.getColumnTypeMap().keySet(); 195 for (String attrKey : attrKeys) { 196 Object o = this.get(attrKey); 197 if (o != null) { 198 m.set(attrKey, o); 199 } 200 } 201 } catch (Exception e) { 202 LOG.error(e.toString(), e); 203 } 204 return m; 205 } 206 207 208 /** 209 * 修复 JFinal use 造成的线程安全问题 210 * 211 * @param configName 212 * @return 213 */ 214 @Override 215 public M use(String configName) { 216 return use(configName, true); 217 } 218 219 220 /** 221 * 优先使用哪个数据源进行查询 222 * 223 * @param configNames 224 * @return 225 */ 226 public M useFirst(String... configNames) { 227 if (configNames == null || configNames.length == 0) { 228 throw new IllegalArgumentException("configNames must not be null or empty."); 229 } 230 231 for (String name : configNames) { 232 M newDao = use(name, false); 233 if (newDao != null) { 234 return newDao; 235 } 236 } 237 return (M) this; 238 } 239 240 241 private M use(String configName, boolean validateDatasourceExist) { 242 243 //非 service 的 dao,例如 new User().user('ds').save()/upate() 244 if (!isDaoModel()) { 245 _setConfigName(configName); 246 return validDatasourceExist((M) this, validateDatasourceExist, configName); 247 } 248 249 //定义在 service 中的 DAO 250 M newDao = JbootModelExts.getDatasourceDAO(this, DATASOURCE_CACHE_PREFIX + configName); 251 if (newDao == null) { 252 newDao = this.copy()._setConfigName(configName); 253 newDao = validDatasourceExist(newDao, validateDatasourceExist, configName); 254 if (newDao != null) { 255 JbootModelExts.setDatasourceDAO(this, DATASOURCE_CACHE_PREFIX + configName, newDao); 256 } 257 } 258 return newDao; 259 } 260 261 262 private M validDatasourceExist(M model, boolean valid, String configName) { 263 if (model._getConfig() == null) { 264 if (valid) { 265 throw new JbootIllegalConfigException("The datasource \"" + configName + "\" not config well, please config it in jboot.properties."); 266 } else { 267 return null; 268 } 269 } 270 return model; 271 } 272 273 274 M _setConfigName(String configName) { 275 this.datasourceName = configName; 276 return (M) this; 277 } 278 279 280 @Override 281 protected Config _getConfig() { 282 if (datasourceName != null) { 283 return DbKit.getConfig(datasourceName); 284 } 285 286 String currentConfigName = JbootDb.getCurrentConfigName(); 287 if (StrUtil.isNotBlank(currentConfigName)) { 288 Config config = DbKit.getConfig(currentConfigName); 289 if (config == null) { 290 LogKit.error("Can not use the datasource: {}, user default to replace.", currentConfigName); 291 } else { 292 return config; 293 } 294 } 295 296 return DbKit.getConfig(_getUsefulClass()); 297 } 298 299 300 public boolean saveOrUpdate() { 301 if (null == _getIdValue()) { 302 return this.save(); 303 } 304 return this.update(); 305 } 306 307 308 @Override 309 public boolean save() { 310 if (_hasColumn(column_created) && get(column_created) == null) { 311 set(column_created, new Date()); 312 } 313 314 // 生成主键,只对单一主键的表生成,如果是多主键,不生成。 315 String[] pkeys = _getPrimaryKeys(); 316 if (pkeys != null && pkeys.length == 1 && get(pkeys[0]) == null) { 317 Object value = config.getPrimarykeyValueGenerator().genValue(this, _getPrimaryType()); 318 if (value != null) { 319 set(pkeys[0], value); 320 } 321 } 322 323 324 filter(FILTER_BY_SAVE); 325 326 Config config = _getConfig(); 327 Table table = _getTable(); 328 329 StringBuilder sql = new StringBuilder(); 330 List<Object> paras = new ArrayList<>(); 331 332 Dialect dialect = _getConfig().getDialect(); 333 dialect.forModelSave(table, _getAttrs(), sql, paras); 334 335 try { 336 return SqlDebugger.run(() -> { 337 Connection conn = null; 338 PreparedStatement pst = null; 339 int result = 0; 340 try { 341 conn = config.getConnection(); 342 if (dialect.isOracle()) { 343 pst = conn.prepareStatement(sql.toString(), table.getPrimaryKey()); 344 } else { 345 pst = conn.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS); 346 } 347 dialect.fillStatement(pst, paras); 348 result = pst.executeUpdate(); 349 dialect.getModelGeneratedKey(this, pst, table); 350 _getModifyFlag().clear(); 351 return result >= 1; 352 } finally { 353 config.close(pst, conn); 354 } 355 }, config, sql.toString(), paras.toArray()); 356 } catch (SQLException e) { 357 throw new ActiveRecordException(e); 358 } 359 } 360 361 362 @Override 363 protected void filter(int filterBy) { 364 config.getFilter().filter(this, filterBy); 365 } 366 367 @Override 368 public M findById(Object idValue) { 369 if (idValue == null) { 370 return null; 371 } 372 return idCacheEnable ? loadByCache(idValue) : super.findById(idValue); 373 } 374 375 /** 376 * 直接查询数据库,不走缓存 377 * 378 * @param idValue 379 * @return 380 */ 381 public M findByIdWithoutCache(Object idValue) { 382 if (idValue == null) { 383 return null; 384 } 385 return super.findById(idValue); 386 } 387 388 389 @Override 390 public M findByIds(Object... idValues) { 391 if (idValues == null) { 392 return null; 393 } 394 if (idValues.length != _getPrimaryKeys().length) { 395 throw new IllegalArgumentException("idValues.length != _getPrimaryKeys().length"); 396 } 397 return idCacheEnable ? loadByCache(idValues) : super.findByIds(idValues); 398 } 399 400 /** 401 * 直接查询数据库,不走缓存 402 * 403 * @param idValues 404 * @return 405 */ 406 public M findByIdsWithoutCache(Object... idValues) { 407 if (idValues == null) { 408 return null; 409 } 410 if (idValues.length != _getPrimaryKeys().length) { 411 throw new IllegalArgumentException("idValues.length != _getPrimaryKeys().length"); 412 } 413 return super.findByIds(idValues); 414 } 415 416 417 protected M loadByCache(Object... idValues) { 418 try { 419 M m = config.getIdCache().get(buildIdCacheName(_getTableName()) 420 , buildIdCacheKey(idValues) 421 , () -> JbootModel.super.findByIds(idValues) 422 , config.getIdCacheTime()); 423 return m != null && config.isIdCacheByCopyEnable() ? m.copy() : m; 424 } catch (Exception ex) { 425 LOG.error("Jboot load model [" + ClassUtil.getUsefulClass(getClass()) + "] by cache is error, safe deleted it in cache.", ex); 426 safeDeleteCache(idValues); 427 } 428 429 return JbootModel.super.findByIds(idValues); 430 } 431 432 433 protected void safeDeleteCache(Object... idValues) { 434 try { 435 config.getIdCache().remove(buildIdCacheName(_getTableName()) 436 , buildIdCacheKey(idValues)); 437 } catch (Exception ex) { 438 LOG.error("Remove cache is error by name [" + buildIdCacheName(_getTableName()) + "] and key [" + buildIdCacheKey(idValues) + "]", ex); 439 } 440 } 441 442 443 @Override 444 public boolean delete() { 445 boolean success = super.delete(); 446 if (success && idCacheEnable) { 447 deleteIdCache(); 448 } 449 return success; 450 } 451 452 @Override 453 public boolean deleteById(Object idValue) { 454 boolean success = super.deleteById(idValue); 455 if (success && idCacheEnable) { 456 deleteIdCacheById(idValue); 457 } 458 return success; 459 } 460 461 @Override 462 public boolean deleteByIds(Object... idValues) { 463 boolean success = super.deleteByIds(idValues); 464 if (success && idCacheEnable) { 465 deleteIdCacheById(idValues); 466 } 467 return success; 468 } 469 470 471 public boolean deleteByColumn(Column column) { 472 if (column == null || !column.checkAvailable()) { 473 throw new IllegalArgumentException("Column or value must not be null."); 474 } 475 return deleteByColumns(Columns.create(column)); 476 } 477 478 479 public boolean deleteByColumns(Columns columns) { 480 processColumns(columns, "delete"); 481 482 if (columns.isEmpty()) { 483 throw new IllegalArgumentException("Columns must not be null or empty."); 484 } 485 String sql = _getDialect().forDeleteByColumns(alias, joins, _getTableName(), columns.getList()); 486 return Db.use(_getConfig().getName()).update(sql, Util.getValueArray(columns.getList())) >= 1; 487 } 488 489 490 public boolean deleteAll() { 491 Columns columns = Columns.create(); 492 493 //通过 processColumns 可以重构 deleteAll 的行为 494 processColumns(columns, "deleteAll"); 495 496 String sql = _getDialect().forDeleteByColumns(alias, joins, _getTableName(), columns.getList()); 497 return Db.use(_getConfig().getName()).update(sql, Util.getValueArray(columns.getList())) >= 1; 498 } 499 500 501 public boolean batchDeleteByIds(Object... idValues) { 502 if (idValues == null || idValues.length == 0) { 503 return false; 504 } 505 boolean success = deleteByColumns(Columns.create().orEqs(_getPrimaryKey(), idValues)); 506 if (success && idCacheEnable) { 507 for (Object id : idValues) { 508 deleteIdCacheById(id); 509 } 510 } 511 return success; 512 } 513 514 515 @Override 516 public boolean update() { 517 if (_hasColumn(column_modified)) { 518 set(column_modified, new Date()); 519 } 520 521 boolean success = super.update(); 522 523 if (success && idCacheEnable) { 524 deleteIdCache(); 525 } 526 527 return success; 528 } 529 530 531 public void deleteIdCache() { 532 if (_getPrimaryKeys().length == 1) { 533 Object idValue = get(_getPrimaryKey()); 534 deleteIdCacheById(idValue); 535 } else { 536 Object[] idvalues = new Object[_getPrimaryKeys().length]; 537 for (int i = 0; i < idvalues.length; i++) { 538 idvalues[i] = get(_getPrimaryKeys()[i]); 539 } 540 deleteIdCacheById(idvalues); 541 } 542 } 543 544 public void deleteIdCacheById(Object... idvalues) { 545 safeDeleteCache(idvalues); 546 } 547 548 549 protected String buildIdCacheName(String orginal) { 550 return orginal; 551 } 552 553 protected String buildIdCacheKey(Object... idValues) { 554 if (idValues == null || idValues.length == 0) { 555 return null; 556 } 557 558 if (idValues.length == 1) { 559 return idValues[0].toString(); 560 } 561 562 StringBuilder key = new StringBuilder(); 563 for (int i = 0; i < idValues.length; i++) { 564 key.append(idValues[i]); 565 if (i < idValues.length - 1) { 566 key.append(":"); 567 } 568 } 569 return key.toString(); 570 } 571 572 protected JbootDialect _getDialect() { 573 Config config = _getConfig(); 574 if (config == null) { 575 return throwCannotMappingException(); 576 } 577 return (JbootDialect) config.getDialect(); 578 } 579 580 581 private JbootDialect throwCannotMappingException() { 582 io.jboot.db.annotation.Table annotation = this.getClass().getAnnotation(io.jboot.db.annotation.Table.class); 583 if (annotation != null && StrUtil.isNotBlank(annotation.datasource())) { 584 throw new JbootException( 585 String.format("Model \"%s\" can not mapping to datasource: " + annotation.datasource() 586 , _getUsefulClass().getName())); 587 } else { 588 throw new JbootException( 589 String.format("Model \"%s\" can not mapping to database table, maybe application cannot connect to database. " 590 , _getUsefulClass().getName())); 591 } 592 } 593 594 595 public M findFirstByColumn(String column, Object value) { 596 return findFirstByColumn(Column.create(column, value)); 597 } 598 599 600 public M findFirstByColumn(String column, Object value, String orderBy) { 601 return findFirstByColumn(Column.create(column, value), orderBy); 602 } 603 604 public M findFirstByColumn(Column column) { 605 if (column == null || !column.checkAvailable()) { 606// throw new IllegalArgumentException("Column or value must not be null."); 607 return null; 608 } 609 return findFirstByColumns(Columns.create(column)); 610 } 611 612 613 public M findFirstByColumn(Column column, String orderBy) { 614 if (column == null || !column.checkAvailable()) { 615// throw new IllegalArgumentException("Column or value must not be null."); 616 return null; 617 } 618 return findFirstByColumns(Columns.create(column), orderBy); 619 } 620 621 622 public M findFirstByColumns(Columns columns) { 623 return findFirstByColumns(columns, null); 624 } 625 626 627 public M findFirstByColumns(Columns columns, String orderby) { 628 return findFirstByColumns(columns, orderby, null); 629 } 630 631 public M findFirstByColumns(Columns columns, String orderby, String loadColumns) { 632 processColumns(columns, "findFirst"); 633 if (StrUtil.isBlank(loadColumns) && this.loadColumns != null) { 634 loadColumns = this.loadColumns; 635 } 636 if (StrUtil.isBlank(loadColumns)) { 637 loadColumns = "*"; 638 } 639 String sql = _getDialect().forFindByColumns(alias, joins, _getTableName(), loadColumns, columns.getList(), orderby, 1); 640 return columns.isEmpty() ? findFirst(sql) : findFirst(sql, columns.getValueArray()); 641 } 642 643 644 public List<M> findListByIds(Object... ids) { 645 if (ids == null || ids.length == 0) { 646 return null; 647 } 648 649 List<M> list = new ArrayList<>(); 650 for (Object id : ids) { 651 if (id.getClass() == int[].class) { 652 findListByIds(list, (int[]) id); 653 } else if (id.getClass() == long[].class) { 654 findListByIds(list, (long[]) id); 655 } else if (id.getClass() == short[].class) { 656 findListByIds(list, (short[]) id); 657 } else { 658 M model = findById(id); 659 if (model != null) { 660 list.add(model); 661 } 662 } 663 } 664 return list; 665 } 666 667 private void findListByIds(List<M> list, int[] ids) { 668 for (int id : ids) { 669 M model = findById(id); 670 if (model != null) { 671 list.add(model); 672 } 673 } 674 } 675 676 private void findListByIds(List<M> list, long[] ids) { 677 for (long id : ids) { 678 M model = findById(id); 679 if (model != null) { 680 list.add(model); 681 } 682 } 683 } 684 685 686 private void findListByIds(List<M> list, short[] ids) { 687 for (short id : ids) { 688 M model = findById(id); 689 if (model != null) { 690 list.add(model); 691 } 692 } 693 } 694 695 696 public List<M> findListByColumn(String column, Object value) { 697 return findListByColumn(Column.create(column, value), null, null); 698 } 699 700 public List<M> findListByColumn(Column column) { 701 return findListByColumn(column, null, null); 702 } 703 704 705 public List<M> findListByColumn(String column, Object value, Integer count) { 706 return findListByColumn(Column.create(column, value), null, count); 707 } 708 709 public List<M> findListByColumn(Column column, Integer count) { 710 return findListByColumn(column, null, count); 711 } 712 713 714 public List<M> findListByColumn(String column, Object value, String orderBy) { 715 return findListByColumn(Column.create(column, value), orderBy, null); 716 } 717 718 719 public List<M> findListByColumn(Column column, String orderby) { 720 return findListByColumn(column, orderby, null); 721 } 722 723 public List<M> findListByColumn(String column, Object value, String orderBy, Integer count) { 724 return findListByColumn(Column.create(column, value), orderBy, count); 725 } 726 727 public List<M> findListByColumn(Column column, String orderBy, Integer count) { 728 if (column == null || !column.checkAvailable()) { 729 return null; 730 } 731 return findListByColumns(Columns.create(column), orderBy, count); 732 } 733 734 735 public List<M> findListByColumns(List<Column> columns) { 736 return findListByColumns(columns, null, null); 737 } 738 739 public List<M> findListByColumns(List<Column> columns, String orderBy) { 740 return findListByColumns(columns, orderBy, null); 741 } 742 743 public List<M> findListByColumns(List<Column> columns, Integer count) { 744 return findListByColumns(columns, null, count); 745 } 746 747 public List<M> findListByColumns(List<Column> columns, String orderBy, Integer count) { 748 return findListByColumns(Columns.create(columns), orderBy, count); 749 } 750 751 public List<M> findListByColumns(Columns columns) { 752 return findListByColumns(columns, null, null); 753 } 754 755 public List<M> findListByColumns(Columns columns, String orderBy) { 756 return findListByColumns(columns, orderBy, null); 757 } 758 759 public List<M> findListByColumns(Columns columns, Integer count) { 760 return findListByColumns(columns, null, count); 761 } 762 763 public List<M> findListByColumns(Columns columns, String orderBy, Integer count) { 764 return findListByColumns(columns, orderBy, count, null); 765 } 766 767 public List<M> findListByColumns(Columns columns, String orderBy, Integer count, String loadColumns) { 768 processColumns(columns, "findList"); 769 loadColumns = getLoadColumns(loadColumns); 770 String sql = _getDialect().forFindByColumns(alias, joins, _getTableName(), loadColumns, columns.getList(), orderBy, count); 771 return columns.isEmpty() ? find(sql) : find(sql, columns.getValueArray()); 772 } 773 774 //方便在某些场景下,对 columns 进行二次加工 775 protected void processColumns(Columns columns, String action) { 776 } 777 778 @Override 779 protected Class<? extends Model> _getUsefulClass() { 780 Class c = getClass(); 781 // guice : Model$$EnhancerByGuice$$40471411 782 // cglib : com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158 783 // return c.getName().indexOf("EnhancerByCGLIB") == -1 ? c : c.getSuperclass(); 784 // return c.getName().indexOf("$$EnhancerBy") == -1 ? c : c.getSuperclass(); 785 786 //不支持匿名类,匿名无法被创建 787 return c.getName().indexOf("$") == -1 ? c : c.getSuperclass(); 788 } 789 790 private String getLoadColumns(String loadColumns) { 791 if (StrUtil.isBlank(loadColumns) && StrUtil.isNotBlank(this.loadColumns)) { 792 loadColumns = this.loadColumns; 793 } 794 795 //使用 join 的情况下,需要判断 distinct 796 if (hasAnyJoinEffective()) { 797 String distinctColumn = JbootModelExts.getDistinctColumn(this); 798 799 //用户配置了 distinct 800 if (StrUtil.isNotBlank(distinctColumn)) { 801 if (StrUtil.isBlank(loadColumns)) { 802 loadColumns = (StrUtil.isNotBlank(alias) ? alias : _getTableName()) + ".*"; 803 } 804 805 //用户配置的 loadColumns 未包含 distinct 关键字 806 if (!loadColumns.toLowerCase().contains("distinct ")) { 807 loadColumns = "DISTINCT " + distinctColumn + ", " + loadColumns; 808 } 809 } 810 } 811 812 if (StrUtil.isBlank(loadColumns)) { 813 loadColumns = "*"; 814 } 815 816 return loadColumns; 817 } 818 819 820 boolean hasAnyJoinEffective() { 821 if (joins == null || joins.size() == 0) { 822 return false; 823 } 824 825 for (Join join : joins) { 826 if (join.isEffective()) { 827 return true; 828 } 829 } 830 831 return false; 832 } 833 834 835 public Page<M> paginate(int pageNumber, int pageSize) { 836 return paginateByColumns(pageNumber, pageSize, Columns.create(), null); 837 } 838 839 840 public Page<M> paginate(int pageNumber, int pageSize, String orderBy) { 841 return paginateByColumns(pageNumber, pageSize, Columns.create(), orderBy); 842 } 843 844 845 public Page<M> paginateByColumn(int pageNumber, int pageSize, Column column) { 846 return paginateByColumns(pageNumber, pageSize, Columns.create(column), null); 847 } 848 849 850 public Page<M> paginateByColumn(int pageNumber, int pageSize, Column column, String orderBy) { 851 return paginateByColumns(pageNumber, pageSize, Columns.create(column), orderBy); 852 } 853 854 855 public Page<M> paginateByColumns(int pageNumber, int pageSize, Columns columns) { 856 return paginateByColumns(pageNumber, pageSize, columns, null); 857 } 858 859 860 public Page<M> paginateByColumns(int pageNumber, int pageSize, List<Column> columns) { 861 return paginateByColumns(pageNumber, pageSize, columns, null); 862 } 863 864 865 public Page<M> paginateByColumns(int pageNumber, int pageSize, List<Column> columns, String orderBy) { 866 return paginateByColumns(pageNumber, pageSize, Columns.create(columns), orderBy); 867 } 868 869 870 public Page<M> paginateByColumns(int pageNumber, int pageSize, Columns columns, String orderBy) { 871 return paginateByColumns(pageNumber, pageSize, columns, orderBy, null); 872 } 873 874 public Page<M> paginateByColumns(int pageNumber, int pageSize, Columns columns, String orderBy, String loadColumns) { 875 processColumns(columns, "paginate"); 876 877 loadColumns = getLoadColumns(loadColumns); 878 879 880 String selectPartSql = _getDialect().forPaginateSelect(loadColumns); 881 String fromPartSql = _getDialect().forPaginateFrom(alias, joins, _getTableName(), columns.getList(), orderBy); 882 883// return columns.isEmpty() 884// ? paginate(pageNumber, pageSize, selectPartSql, fromPartSql) 885// : paginate(pageNumber, pageSize, selectPartSql, fromPartSql, columns.getValueArray()); 886 887 Config config = _getConfig(); 888 Connection conn = null; 889 try { 890 conn = config.getConnection(); 891// String totalRowSql = config.dialect.forPaginateTotalRow(select, sqlExceptSelect, this); 892 String totalRowSqlExceptSelect = _getDialect().forPaginateFrom(alias, joins, _getTableName(), columns.getList(), null); 893 String totalRowSql = config.getDialect().forPaginateTotalRow(selectPartSql, totalRowSqlExceptSelect, this); 894 895 StringBuilder findSql = new StringBuilder(); 896 findSql.append(selectPartSql).append(' ').append(fromPartSql); 897 898 return doPaginateByFullSql(config, conn, pageNumber, pageSize, null, totalRowSql, findSql, columns.getValueArray()); 899 } catch (Exception e) { 900 throw new ActiveRecordException(e); 901 } finally { 902 config.close(conn); 903 } 904 } 905 906 907 public long findCountByColumn(Column column) { 908 return findCountByColumns(Columns.create(column)); 909 } 910 911 912 public long findCountByColumns(Columns columns) { 913 processColumns(columns, "findCount"); 914 915 String loadColumns = "*"; 916 917 //使用 distinct 918 if (hasAnyJoinEffective()) { 919 String distinctColumn = JbootModelExts.getDistinctColumn(this); 920 if (StrUtil.isNotBlank(distinctColumn)) { 921 loadColumns = "DISTINCT " + distinctColumn; 922 } 923 } 924 925 926 String sql = _getDialect().forFindCountByColumns(alias, joins, _getTableName(), loadColumns, columns.getList()); 927 Long value = Db.use(_getConfig().getName()).queryLong(sql, Util.getValueArray(columns.getList())); 928 return value == null ? 0 : value; 929 } 930 931 932 public <T> T _getIdValue() { 933 return get(_getPrimaryKey()); 934 } 935 936 937 public Object[] _getIdValues() { 938 String[] pkeys = _getPrimaryKeys(); 939 Object[] values = new Object[pkeys.length]; 940 941 int i = 0; 942 for (String key : pkeys) { 943 values[i++] = get(key); 944 } 945 return values; 946 } 947 948 949 public String _getTableName() { 950 return _getTable(true).getName(); 951 } 952 953 @Override 954 public Table _getTable() { 955 return _getTable(false); 956 } 957 958 private transient Table table; 959 960 public Table _getTable(boolean validateMapping) { 961 if (table == null) { 962 table = super._getTable(); 963 if (table == null && validateMapping) { 964 throwCannotMappingException(); 965 } 966 } 967 return table; 968 } 969 970 971 public String _getPrimaryKey() { 972 return _getPrimaryKeys()[0]; 973 } 974 975 private transient String[] primaryKeys; 976 977 public String[] _getPrimaryKeys() { 978 if (primaryKeys != null) { 979 return primaryKeys; 980 } 981 primaryKeys = _getTable(true).getPrimaryKey(); 982 983 if (primaryKeys == null) { 984 throw new JbootException(String.format("primaryKeys == null in [%s]", getClass())); 985 } 986 return primaryKeys; 987 } 988 989 990 private transient Class<?> primaryType; 991 992 protected Class<?> _getPrimaryType() { 993 if (primaryType == null) { 994 primaryType = _getTable(true).getColumnType(_getPrimaryKey()); 995 } 996 return primaryType; 997 } 998 999 1000 protected boolean _hasColumn(String columnLabel) { 1001 return _getTable(true).hasColumnLabel(columnLabel); 1002 } 1003 1004 1005 @Override 1006 public boolean equals(Object o) { 1007 if (!(o instanceof JbootModel)) { 1008 return false; 1009 } 1010 1011 //可能model在rpc的Controller层,没有映射到数据库 1012 if (_getTable(false) == null) { 1013 return this == o; 1014 } 1015 1016 Object id = ((JbootModel<?>) o)._getIdValue(); 1017 return id != null && id.equals(this._getIdValue()); 1018 } 1019 1020 1021 @Override 1022 public int hashCode() { 1023 //可能model在rpc的Controller层,没有映射到数据库 1024 if (_getTable(false) == null) { 1025 return Objects.hash(_getAttrValues()); 1026 } 1027 1028 final Object[] idValues = _getIdValues(); 1029 return idValues.length > 0 ? Objects.hash(idValues) : Objects.hash(_getAttrValues()); 1030 } 1031 1032 public M preventXssAttack() { 1033 String[] attrNames = _getAttrNames(); 1034 for (String attrName : attrNames) { 1035 Object value = get(attrName); 1036 if (!(value instanceof String)) { 1037 continue; 1038 } 1039 1040 set(attrName, StrUtil.escapeHtml((String) value)); 1041 } 1042 return (M) this; 1043 } 1044 1045 1046 public M preventXssAttack(String... ignoreAttrs) { 1047 String[] attrNames = _getAttrNames(); 1048 for (String attrName : attrNames) { 1049 Object value = get(attrName); 1050 if (!(value instanceof String)) { 1051 continue; 1052 } 1053 1054 boolean isIgnoreAttr = false; 1055 for (String ignoreAttr : ignoreAttrs) { 1056 if (attrName.equals(ignoreAttr)) { 1057 isIgnoreAttr = true; 1058 break; 1059 } 1060 } 1061 1062 if (!isIgnoreAttr) { 1063 set(attrName, StrUtil.escapeHtml((String) value)); 1064 } 1065 } 1066 1067 return (M) this; 1068 } 1069 1070 1071 /** 1072 * Override for print sql 1073 * 1074 * @param config 1075 * @param conn 1076 * @param sql 1077 * @param paras 1078 * @return 1079 * @throws Exception 1080 */ 1081 @Override 1082 protected List<M> find(Config config, Connection conn, String sql, Object... paras) throws Exception { 1083 return SqlDebugger.run(() -> { 1084 try { 1085 return super.find(config, conn, sql, paras); 1086 } catch (Exception e) { 1087 if (e instanceof SQLException) { 1088 throw (SQLException) e; 1089 } else { 1090 throw new SQLException(e); 1091 } 1092 } 1093 }, config, sql, paras); 1094 } 1095 1096}