jooq实践
用法
sql语句
SELECT AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME, COUNT(*)
FROM AUTHOR
JOIN BOOK ON AUTHOR.ID = BOOK.AUTHOR_ID
WHERE BOOK.LANGUAGE = 'DE'
AND BOOK.PUBLISHED > DATE '2008-01-01'
GROUP BY AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME
HAVING COUNT(*) > 5
ORDER BY AUTHOR.LAST_NAME ASC NULLS FIRST
LIMIT 2
OFFSET 1
java代码
dsl.select(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME, count())
.from(AUTHOR)
.join(BOOK).on(AUTHOR.ID.equal(BOOK.AUTHOR_ID))
.where(BOOK.LANGUAGE.eq("DE"))
.and(BOOK.PUBLISHED.gt(date("2008-01-01")))
.groupBy(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
.having(count().gt(5))
.orderBy(AUTHOR.LAST_NAME.asc().nullsFirst())
.limit(2)
.offset(1)
示例代码
增:
public void addMerchantUrgentIsochrone(ITbMerchantUrgentIsochrone merchantUrgentIsochrone) {
TbMerchantUrgentIsochroneRecord record = dsl.newRecord(TB_MERCHANT_URGENT_ISOCHRONE, merchantUrgentIsochrone);
record.store();
merchantUrgentIsochrone.setId(record.getId());
}
删:
public int deleteRecords() {
return dsl.delete(TB_MERCHANT_URGENT_ISOCHRONE).where(TB_MERCHANT_URGENT_ISOCHRONE.CREATED_AT.ge(Timestamp.valueOf(LocalDateTime.now().minusDays(1)))).execute();
}
改:
public int update(ITbUserArea userArea) {
return dsl.update(TB_USER_AREA)
.set(TB_USER_AREA.GEOHASH, userArea.getGeohash())
.set(TB_USER_AREA.AREA, userArea.getArea())
.set(TB_USER_AREA.CITY_ID, userArea.getCityId())
.set(TB_USER_AREA.ORDER_COUNT, userArea.getOrderCount())
.where(TB_USER_AREA.ID.eq(userArea.getId()))
.execute();
}
查:
public List<ITbUserArea> getAreas(String preHash) {
return dsl.selectFrom(TB_USER_AREA)
.where(TB_USER_AREA.GEOHASH.like(preHash+"%"))
.fetchInto(TbUserArea.class);
}
jooq 可以执行sql语句
Result<Record> fetch(String var1) throws DataAccessException;
int execute(String var1) throws DataAccessException;
配置篇
maven配置
Maven依赖:(版本号可配)
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>3.9.5</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-meta</artifactId>
<version>3.9.5</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen</artifactId>
<version>3.9.5</version>
</dependency> 工具生成映射配置:
建立 xxx.xml(名字任意)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration>
<jdbc>
<driver>...</driver>
<url>...</url>
<user>...</user>
<password>...</password>
</jdbc>
<generator>
<!-- 指定代码风格
- org.jooq.util.ScalaGenerator
- org.jooq.util.JavaGenerator
-->
<name>org.jooq.util.JavaGenerator</name> <database>
<!-- 数据库类型 -->
<name>org.jooq.util.postgres.PostgresDatabase</name>
<!-- <name>org.jooq.util.mysql.MySQLDatabase</name> -->
<excludes>
<!-- 对tb_table1,tb_table2,tb_table3 表不生成代码-->
tb_table1|tb_table2|tb_table3
</excludes>
<includeExcludeColumns>true</includeExcludeColumns>
<inputSchema>public</inputSchema> <!-- jooq转换对象类型,如用enum代替int
目标转换类型应实现org.jooq.Converter或org.jooq.Binding接口
相应的配置标签为<customType>,<forcedType>
参考地址: https://www.jooq.org/doc/3.9/manual/code-generation/custom-data-types/
https://www.jooq.org/doc/3.9/manual/code-generation/custom-data-type-bindings/
-->
<customTypes>
<customType>
<name>EffectiveStatus</name>
<type>xxx.isochrone.constant.EffectiveStatus</type>
<converter>xxx.geo.jooq.converter.EffectiveStatusConverter</converter>
</customType>
</customTypes>
<forcedTypes>
<!-- 使用converter -->
<forcedType>
<name>EffectiveStatus</name>
<expressions>.*\.tb_isochrone_audit_info\.effective_status</expressions>
<types>.*</types>
</forcedType>
<!-- 使用binding -->
<forcedType>
<userType>xxx.isochrone.pojos.GeographyPolygon</userType>
<binding>xxx.geo.jooq.binding.PGgeometryPolygonBinding</binding>
<expression>.*\.tb_isochrone.range|.*\.tb_merchant_area_isochrone.range</expression>
<types>.*</types>
</forcedType>
</forcedTypes>
</database> <generate>
<deprecated>false</deprecated>
<daos>true</daos>
<interfaces>true</interfaces>
</generate>
<target>
<!-- 生成的包名,生成的类在此包下 -->
<packageName>xxx.isochrone.jooq</packageName>
<!-- 输出的目录 -->
<directory>src/main/java</directory>
</target>
</generator>
</configuration> 相应pom.xml里添加对应的配置信息
<profiles>
<profile>
<id>db-gen</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<includeProjectDependencies>false</includeProjectDependencies>
<includePluginDependencies>true</includePluginDependencies>
<mainClass>org.jooq.util.GenerationTool</mainClass>
<cleanupDaemonThreads>false</cleanupDaemonThreads>
<arguments>
<!-- 匹配工具生成映射配置文件 -->
<argument>xxx.xml</argument>
</arguments>
</configuration>
<dependencies>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>3.9.5</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen</artifactId>
<version>3.9.5</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-meta</artifactId>
<version>3.9.5</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1208.jre7</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</profile>
</profiles>
执行代码自动生成命令
mvn -P db-gen exec:java
gradle配置
application.yml
spring:
jooq:
#请勿动,当使用了ejdbc的配置后,Spring Boot无法识别正确的Dialect
sqlDialect: Mysql
jooq模块下的gradle文件
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
resolutionStrategy {
cacheChangingModulesFor 0, 'seconds'
}
imports {
mavenBom 'io.spring.platform:platform-bom:Brussels-SR6'
mavenBom 'org.jooq:jooq:3.10.1'
}
dependencies {
dependency 'org.springframework.statemachine:spring-statemachine-core:1.2.6.RELEASE'
}
} apply plugin: 'nu.studer.jooq' emodule {
dependency 'tms-jooq-model'
}
dependencies {
compile 'org.jooq:jooq'
jooqRuntime 'mysql:mysql-connector-java'
} // Jooq Gradle Plugin 参考文档 https://github.com/etiennestuder/gradle-jooq-plugin
jooq {
version = '3.10.1'
tms(sourceSets.main) {
jdbc {
driver = 'com.mysql.jdbc.Driver'
url = 'jdbc:mysql://localhost:3306/test'
user = 'xxx'
password = 'xxx'
}
generator {
name = 'org.jooq.util.DefaultGenerator'
database {
name = 'org.jooq.util.mysql.MySQLDatabase'
inputSchema = 'scm_tms'
outputSchemaToDefault = true
includeExcludeColumns = true
//重构,相关字段迁移到tb_docker_deployment_log中
excludes = ".*_bak|.*\\.drc_check_time|.*\\.is_delete|.*\\.updated_at" forcedTypes {
forcedType {
userType = "xxx.common.model.model.GeoPoint"
converter = "xxx.jooq.model.converter.GeoPointConverter"
expression = ".*_lnglat"
types = ".*"
}
forcedType {
userType = "xxx.base.api.model.UsedType"
converter = "xxx.jooq.model.converter.VehicleIsUsedTypeConverter"
/*A Java regular expression matching fully-qualified columns. Use the pipe to separate several expressions. ->database.table.column*/
expression = ".*\\.base_vehicle\\.is_used"
/*Add a Java regular expression matching data types to be forced to have this type*/
types = ".*"
}
}
}
generate {
relations = true
deprecated = false
records = true
interfaces = true
pojos = true
daos = false
fluentSetters = true
}
target {
packageName = project.groupPrefix + "." + project.groupName + ".db.model"
directory = 'src/main/java'
}
}
}
} //不自动执行Jooq的代码生成和清除任务
//数据库更改后,手动进入 jooq模块
//执行 gradle cleanGenerateTmsJooqSchemaSource generateTmsJooqSchemaSource
//生成数据库对象在tms-db-model下
project.tasks.getByName('compileJava').dependsOn -= 'generateTmsJooqSchemaSource'
project.tasks.getByName('clean').dependsOn -= 'cleanGenerateTmsJooqSchemaSource'
jooq Converter示例
import xxx.GeoPoint;
import org.jooq.Converter; public class GeoPointConverter implements Converter<String, GeoPoint> {
@Override
public GeoPoint from(String databaseObject) {
return new GeoPoint(databaseObject);
} @Override
public String to(GeoPoint point) {
return point==null?"":point.gdFormat();
} @Override
public Class<String> fromType() {
return String.class;
} @Override
public Class<GeoPoint> toType() {
return GeoPoint.class;
}
} public class GeoPoint {
/**
* 经度值
*/
private Double longitude;
/**
* 纬度值
*/
private Double latitude; public GeoPoint() {
} public GeoPoint(String location) {
if (!StringUtils.isEmpty(location)) {
String[] lnglat = location.split(",");
this.longitude = Double.valueOf(lnglat[0]);
this.latitude = Double.valueOf(lnglat[1]);
}
} public Double getLongitude() {
return longitude;
} public void setLongitude(Double longitude) {
this.longitude = longitude;
} public Double getLatitude() {
return latitude;
} public void setLatitude(Double latitude) {
this.latitude = latitude;
} public String gdFormat() {
if (longitude == null || latitude == null) {
return "";
}
return longitude + "," + latitude;
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; GeoPoint geoPoint = (GeoPoint) o; if (longitude != null ? !longitude.equals(geoPoint.longitude) : geoPoint.longitude != null) return false;
return latitude != null ? latitude.equals(geoPoint.latitude) : geoPoint.latitude == null;
} @Override
public int hashCode() {
int result = longitude != null ? longitude.hashCode() : 0;
result = 31 * result + (latitude != null ? latitude.hashCode() : 0);
return result;
} @Override
public String toString() {
return "GeoPoint{" +
"longitude=" + longitude +
", latitude=" + latitude +
'}';
} }
import xxx.scm.tms.common.model.model.WarehouseType;
import org.jooq.Converter; /**
* WarehouseTypeConverter
*/
public class WarehouseTypeConverter implements Converter<Byte, WarehouseType> {
@Override
public WarehouseType from(Byte databaseObject) {
return WarehouseType.getTypeByCode(databaseObject);
} @Override
public Byte to(WarehouseType warehouseType) {
return warehouseType.getCode();
} @Override
public Class<Byte> fromType() {
return Byte.class;
} @Override
public Class<WarehouseType> toType() {
return WarehouseType.class;
}
} import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map; public enum WarehouseType {
NORMAL_TEMPERATURE((byte)1, "常温"),
COLD_STORAGE((byte)2, "冷藏"),
FREEZING((byte)3, "冷冻"); private byte code;
private String label; WarehouseType(byte code, String label) {
this.code = code;
this.label = label;
} public byte getCode() {
return code;
} public String getLabel() {
return label;
} private static Map<Byte, WarehouseType> map = new HashMap<>();
static {
EnumSet.allOf(WarehouseType.class)
.forEach(e -> map.put(e.getCode(), e));
} public static WarehouseType getTypeByCode(Byte code) {
return map.get(code);
}
}
高级用法代码示例
public List<BaseWarehouse> searchWarehouses(WarehouseCondition warehouseCondition) {
SelectConditionStep<?> step = dsl.selectFrom(Tables.BASE_WAREHOUSE).where(DSL.trueCondition());
StringBuilder likeValue = new StringBuilder();
if (!Objects.isNull(warehouseCondition.getWarehouseId())) {
step.and(Tables.BASE_WAREHOUSE.WAREHOUSE_ID.eq(warehouseCondition.getWarehouseId()));
}
if (StringUtils.isNotBlank(warehouseCondition.getWarehouseName())) {
likeValue.setLength(0);
likeValue.append("%").append(warehouseCondition.getWarehouseName()).append("%");
step.and(Tables.BASE_WAREHOUSE.WAREHOUSE_NAME.likeIgnoreCase(likeValue.toString()));
}
if (StringUtils.isNotBlank(warehouseCondition.getCompany())) {
likeValue.setLength(0);
likeValue.append("%").append(warehouseCondition.getCompany()).append("%");
step.and(Tables.BASE_WAREHOUSE.COMPANY.likeIgnoreCase(likeValue.toString()));
}
if (StringUtils.isNotBlank(warehouseCondition.getProvinceName())) {
step.and(Tables.BASE_WAREHOUSE.PROVINCE_NAME.eq(warehouseCondition.getProvinceName()));
}
if (!Objects.isNull(warehouseCondition.getProvinceId())) {
step.and(Tables.BASE_WAREHOUSE.PROVINCE_ID.eq(warehouseCondition.getProvinceId()));
}
if (StringUtils.isNotBlank(warehouseCondition.getCityName())) {
step.and(Tables.BASE_WAREHOUSE.CITY_NAME.eq(warehouseCondition.getCityName()));
}
if (!Objects.isNull(warehouseCondition.getCityId())) {
step.and(Tables.BASE_WAREHOUSE.CITY_ID.eq(warehouseCondition.getCityId()));
}
if (StringUtils.isNotBlank(warehouseCondition.getDistrictName())) {
step.and(Tables.BASE_WAREHOUSE.DISTRICT_NAME.eq(warehouseCondition.getDistrictName()));
}
if (!Objects.isNull(warehouseCondition.getDistrictId())) {
step.and(Tables.BASE_WAREHOUSE.DISTRICT_ID.eq(warehouseCondition.getDistrictId()));
}
if (StringUtils.isNotBlank(warehouseCondition.getAddress())) {
likeValue.setLength(0);
likeValue.append("%").append(warehouseCondition.getAddress()).append("%");
step.and(Tables.BASE_WAREHOUSE.ADDRESS.like(likeValue.toString()));
}
if (!Objects.isNull(warehouseCondition.getWarehouseLnglat())) {
step.and(Tables.BASE_WAREHOUSE.WAREHOUSE_LNGLAT.eq(warehouseCondition.getWarehouseLnglat()));
}
if (StringUtils.isNotBlank(warehouseCondition.getContactName())) {
step.and(Tables.BASE_WAREHOUSE.CONTACT_NAME.eq(warehouseCondition.getContactName()));
}
if (StringUtils.isNotBlank(warehouseCondition.getContactPhone())) {
step.and(Tables.BASE_WAREHOUSE.CONTACT_PHONE.eq(warehouseCondition.getContactPhone()));
}
return step.fetchInto(BaseWarehouse.class);
}
private Collection<SelectField<?>> getListField() {
Collection<SelectField<?>> fields = new ArrayList<>();
fields.add(SHIPMENT.SHIPMENT_ID);
fields.add(SHIPMENT.OUTBOUND_ID);
fields.add(SHIPMENT.BIZ_TYPE);
fields.add(SHIPMENT.BIZ_SUBTYPE);
fields.add(SHIPMENT.PICKUP_NAME);
fields.add(SHIPMENT.PICKUP_CONTACT_NAME);
fields.add(SHIPMENT.PICKUP_CONTACT_PHONE);
fields.add(SHIPMENT.PICKUP_PROVINCE_NAME);
fields.add(SHIPMENT.PICKUP_CITY_NAME);
fields.add(SHIPMENT.PICKUP_DISTRICT_NAME);
fields.add(SHIPMENT.PICKUP_ADDRESS);
fields.add(SHIPMENT.DESTINATION_NAME);
fields.add(SHIPMENT.DESTINATION_CONTACT_NAME);
fields.add(SHIPMENT.DESTINATION_CONTACT_PHONE);
fields.add(SHIPMENT.DESTINATION_PROVINCE_NAME);
fields.add(SHIPMENT.DESTINATION_CITY_NAME);
fields.add(SHIPMENT.DESTINATION_DISTRICT_NAME);
fields.add(SHIPMENT.DESTINATION_ADDRESS);
fields.add(SHIPMENT.DESTINATION_REMARK);
fields.add(SHIPMENT.ORDER_ID);
fields.add(SHIPMENT.ORDER_CREATED_AT);
fields.add(SHIPMENT.STATUS);
fields.add(DSL.ifnull(SHIPMENT_SKU.SHIPMENT_ID.count(), 0).as("COUNT"));
fields.add(DSL.ifnull(SHIPMENT_SKU.SKU_COUNT.sum(), BigDecimal.ZERO).as("SKU_COUNT"));
fields.add(DSL.ifnull(SHIPMENT_SKU.OUTBOUND_COUNT.sum(), BigDecimal.ZERO).as("OUTBOUND_COUNT"));
fields.add(DSL.ifnull(SHIPMENT_SKU.SIGN_COUNT.sum(), BigDecimal.ZERO).as("SIGN_COUNT"));
fields.add(SHIPMENT.PICKUP_LNGLAT);
fields.add(SHIPMENT.DESTINATION_LNGLAT);
fields.add(SHIPMENT.EXPECT_ARRIVE_BEGIN_AT);
fields.add(SHIPMENT.EXPECT_ARRIVE_END_AT);
fields.add(SHIPMENT.BIZ_JSON);
fields.add(SHIPMENT.WAREHOUSE_ID);
fields.add(SHIPMENT.DRIVER_REMARK);
//detail使用字段
fields.add(SHIPMENT.DRIVER_ID);
fields.add(SHIPMENT.CUSTOMER_SERVICE);
fields.add(SHIPMENT.CARRIER_ID);
fields.add(SHIPMENT.CARRIER_NAME);
fields.add(SHIPMENT.ROUTE_ID);
return fields;
}
Result<Record> records = dsl.select(getListField())
.from(SHIPMENT)
.leftJoin(SHIPMENT_SKU)
.on(SHIPMENT.SHIPMENT_ID.eq(SHIPMENT_SKU.SHIPMENT_ID))
.where(getListConditions(query))
.groupBy(SHIPMENT.SHIPMENT_ID)
.orderBy(SHIPMENT.CREATED_AT.desc())
.offset(query.getOffset())
.limit(query.getLimit())
.fetch();
DSLContext executeUpdate
dsl.executeUpdate(dsl.newRecord(BASE_WAREHOUSE, baseWarehouse)), 如果vo类中的主键为null的时候

要指定 condition

乐观锁实现
https://www.jooq.org/doc/3.10/manual-single-page/#optimistic-locking
https://www.jooq.org/doc/3.10/manual-single-page/#codegen-config-record-version-timestamp-fields
jooq实践的更多相关文章
- 10个精妙的Java编码最佳实践
这是一个比Josh Bloch的Effective Java规则更精妙的10条Java编码实践的列表.和Josh Bloch的列表容易学习并且关注日常情况相比,这个列表将包含涉及API/SPI设计中不 ...
- 你知道吗?10个精妙的 Java 编码最佳实践
这是一个比Josh Bloch的Effective Java规则更精妙的10条Java编码实践的列表.和Josh Bloch的列表容易学习并且关注日常情况相比,这个列表将包含涉及API/SPI设计中不 ...
- 漫谈Code Review的错误实践
从刚开始工作时到现在,已经写了7年的代码,大部分代码都被人review过,自己也review了很多人的代码.在上一家公司的时候,我负责的一轮面试是专门进行Code Review的练习和经验谈. 通过在 ...
- 【转载】 漫谈Code Review的错误实践
原文地址: https://www.cnblogs.com/chaosyang/p/code-review-wrong-practices.html ------------------------- ...
- webp图片实践之路
最近,我们在项目中实践了webp图片,并且抽离出了工具模块,整合到了项目的基础模板中.传闻IOS10也将要支持webp,那么使用webp带来的性能提升将更加明显.估计在不久的将来,webp会成为标配. ...
- Hangfire项目实践分享
Hangfire项目实践分享 目录 Hangfire项目实践分享 目录 什么是Hangfire Hangfire基础 基于队列的任务处理(Fire-and-forget jobs) 延迟任务执行(De ...
- TDD在Unity3D游戏项目开发中的实践
0x00 前言 关于TDD测试驱动开发的文章已经有很多了,但是在游戏开发尤其是使用Unity3D开发游戏时,却听不到特别多关于TDD的声音.那么本文就来简单聊一聊TDD如何在U3D项目中使用以及如何使 ...
- Logstash实践: 分布式系统的日志监控
文/赵杰 2015.11.04 1. 前言 服务端日志你有多重视? 我们没有日志 有日志,但基本不去控制需要输出的内容 经常微调日志,只输出我们想看和有用的 经常监控日志,一方面帮助日志微调,一方面及 ...
- 【大型网站技术实践】初级篇:借助Nginx搭建反向代理服务器
一.反向代理:Web服务器的“经纪人” 1.1 反向代理初印象 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从 ...
随机推荐
- 关于Linux 虚拟机如何才能ping 通外网
需要虚拟机能够联网.以前都是用桥接模式让虚拟机跟主机通信,这几天查了好多资料,都没有写得很详细,自己捣鼓了很久,把步骤写下来吧. 虚拟机操作步骤: 点击虚拟机的“菜单栏”上的“编辑”,再点击“虚拟网络 ...
- jquery选择器最后一个,倒数第二个元素
<div> <p>1</p> <p>2</p> <p>3</p> <p>4</p> < ...
- Java获取资源路径——(八)
获取文件资源有两种方式: 第一种是: 获取Java项目根目录开始制定文件夹下指定文件,不用类加载器(目录开始要加/) // 获取工程路径 System.out.println(System.getP ...
- TabCtrl使用
TabCtrl使用 0x1 新建子页面 插入三个对话框,ID分别为:IDD_PAGE_FILE.IDD_PAGE_NETWORK.IDD_PAGE_PROCESS 工具箱-[属性]-[Style]设置 ...
- C++学习2--坦克大战编写-前置知识
基础班学习的这一个多月里的前三周讲解基础的语法,最后一周需要做坦克大战的项目巩固提高自己掌握的语法知识.这个系列博文主要是为了把学习过程中的知识点总结并记录下来: 开发语言与开发工具:C++,VS20 ...
- Linux 入门记录:十二、Linux 权限机制【转】
转自:https://www.cnblogs.com/mingc/p/7591287.html 一.权限 权限是操作系统用来限制资源访问的机制,权限一般分为读.写.执行. 系统中每个文件都拥有特定的权 ...
- 转载:《理解RESTful架构》 阮一峰
原文:http://www.ruanyifeng.com/blog/2011/09/restful.html 越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件&q ...
- Future、 CompletableFuture、ThreadPoolTaskExecutor简单实践
一 Future(jdk5引入) 简介: Future接口是Java多线程Future模式的实现,可以来进行异步计算. 可以使用isDone方法检查计算是否完成,或者使用get阻塞住调用线程,直到计算 ...
- vue2进阶之v-model在组件上的使用
v-model 用在 input 元素上时 v-model虽然很像使用了双向数据绑定的 Angular 的 ng-model,但是 Vue 是单项数据流,v-model 只是语法糖而已: <in ...
- 搭建ssh框架项目(二)
一.创建dao层 (1)创建接口ICommonDao.java package com.cppdy.ssh.dao; public interface ICommonDao<T> { pu ...