参考:https://segmentfault.com/a/1190000006748584?utm_source=tuicool&utm_medium=referral
JOOQ是啥
JOOQ 是基于Java访问关系型数据库的工具包。JOOQ 既吸取了传统ORM操作数据的简单性和安全性,又保留了原生sql的灵活性,它更像是介于 ORMS和JDBC的中间层。对于喜欢写sql的码农来说,JOOQ可以完全满足你控制欲,可以是用Java代码写出sql的感觉来。就像官网说的那样 :
get back in control of your sql
1.Springboot整合jooq首先加入jar包依赖
<!-- mysql orm pool --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.41</version> </dependency> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>2.6.2</version> </dependency> <dependency> <groupId>org.jooq</groupId> <artifactId>jooq</artifactId> <version>3.9.1</version> </dependency>
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/modeo?useUnicode=true&characterEncoding=UTF-8&useSSL=false username: modeo password: 123456
2.在项目根目录下建立一个文件夹generator目录下加入以下jar包以及建立mysql.xml(一会用到)
3.编辑mysql.xml文件
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <configuration xmlns="http://www.jooq.org/xsd/jooq-codegen-3.8.0.xsd"> <!-- 配置jdbc驱动连接 --> <jdbc> <driver>com.mysql.jdbc.Driver</driver> <url>jdbc:mysql://localhost:3306/admin</url> <user>root</user> <password>123456</password> </jdbc> <generator> <!-- 代码生成器 --> <name>org.jooq.util.JavaGenerator</name> <database> <!-- 数据库类型 --> <name>org.jooq.util.mysql.MySQLDatabase</name> <!-- 数据库名 --> <inputSchema>admin</inputSchema> <!-- 生成包含,*表示包含所有内容 --> <includes>.*</includes> <!--剔除,此处未剔除 --> <excludes></excludes> </database> <target> <!-- 生成的代码所在的包结构 --> <packageName>org.test.jooq.generated</packageName> <!-- 生成的代码存放路径,默认会以src同目录开始 --> <directory>src/main/java/</directory> </target> <strategy> <matchers> <tables> <table> <expression>^(.*)$</expression> <tableClass> <transform>PASCAL</transform> <expression>$1</expression> </tableClass> <recordClass> <transform>PASCAL</transform> <expression>$1_P_O</expression> </recordClass> </table> </tables> </matchers> </strategy> </generator> </configuration>
4.创建MysqlManager数据库操作工具
public class MysqlManager implements Closeable { private static final String DB_DRIVER_NAME = "spring.datasource.driver-class-name"; private static final String DB_URL = "spring.datasource.url"; private static final String DB_USERNAME = "spring.datasource.username"; private static final String DB_PASSWORD = "spring.datasource.password"; /** * Singleton */ private static final MysqlManager INSTANCE = new MysqlManager(); /** * database configuration */ private Configuration configuration = null; /** * database source */ private HikariDataSource dataSource = null; /** * get DSL context * * @return DSLContext */ public DSLContext getContext() { return DSL.using(this.configuration); } /** * Constructor */ private MysqlManager() { this.dataSource = new HikariDataSource(); this.dataSource.setDriverClassName(Config.getProperty(DB_DRIVER_NAME)); this.dataSource.setJdbcUrl(Config.getProperty(DB_URL)); this.dataSource.setUsername(Config.getProperty(DB_USERNAME)); this.dataSource.setPassword(Config.getProperty(DB_PASSWORD)); // 设置连接池的最大/最小 size this.dataSource.addDataSourceProperty("maxConnectionsPerPartition", "5"); this.dataSource.addDataSourceProperty("minConnectionsPerPartition", "1"); this.dataSource.addDataSourceProperty("idleConnectionTestPeriodInMinutes", "10"); this.dataSource.addDataSourceProperty("maxConnectionAgeInSeconds", "3600"); this.dataSource.addDataSourceProperty("idleMaxAgeInMinutes", "300"); this.dataSource.addDataSourceProperty("cachePrepStmts", "true"); this.dataSource.addDataSourceProperty("prepStmtCacheSize", "250"); this.dataSource.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); this.dataSource.addDataSourceProperty("connectionTimeout", "3000"); TransactionAwareDataSourceProxy proxy = new TransactionAwareDataSourceProxy(this.dataSource); DataSourceTransactionManager txMgr = new DataSourceTransactionManager(this.dataSource); this.configuration = new DefaultConfiguration().set(new DataSourceConnectionProvider(proxy)) .set(new SpringTransactionProvider(txMgr)).set(SQLDialect.MYSQL); } /** * Singleton * * @return instance */ public static MysqlManager getInstance() { return INSTANCE; } /** * transaction *做事物处理时用 * @param runnable */ public static void transaction(Runnable runnable) { DSLContext context = MysqlManager.getInstance().getContext(); context.transaction(config -> runnable.run()); } /** * Closes this stream and releases any system resources associated * with it. If the stream is already closed then invoking this * method has no effect. * * <p> As noted in {@link AutoCloseable#close()}, cases where the * close may fail require careful attention. It is strongly advised * to relinquish the underlying resources and to internally * <em>mark</em> the {@code Closeable} as closed, prior to throwing * the {@code IOException}. * * @throws IOException if an I/O error occurs */ @Override public void close() throws IOException { if (this.dataSource != null && !this.dataSource.isClosed()) { this.dataSource.close(); this.dataSource = null; } this.configuration = null; } }
5.在linux系统下在创建的generator目录下运行以下命令就会生成数据库表对应的操作对象(在mysql.xml配置的路径下)
cd generator java -classpath jooq-3.9.1.jar:jooq-meta-3.9.1.jar:jooq-codegen-3.9.1.jar:mysql-connector-java-5.1.41.jar:. org.jooq.util.GenerationTool mysql.xml
6.具体的数据库操作案例:
(1.)使用事物处理
private static boolean removeSubscribedSymbol(String deviceId, List<MarketSymbolVO> exchangeSymbols) { if (exchangeSymbols == null || exchangeSymbols.isEmpty()) { return true; } DSLContext context = MysqlManager.getInstance().getContext(); GuestSubscribeMarket table = GuestSubscribeMarket.GUEST_SUBSCRIBE_MARKET; MysqlManager.transaction(() -> { for (MarketSymbolVO item : exchangeSymbols) { context.deleteFrom(table).where(table.DEVICE_ID.eq(deviceId), table.TYPE.eq(item.getType()), table.TARGET_ID.eq(item.getId())).execute(); } }); return true; }
(2)一般的操作
public static boolean hasSubscribedSymbol(String deviceId) { DSLContext context = MysqlManager.getInstance().getContext(); GuestSubscribeMarket table = GuestSubscribeMarket.GUEST_SUBSCRIBE_MARKET; return context.selectFrom(table).where(table.DEVICE_ID.eq(deviceId)).fetchAny() != null; }
(3)jooq支持纯sql的模式
/** * @param period * @param coinId * @return */ public static List<CoinKlineDO> listCoinKlineDO(String period, int coinId) { DSLContext context = MysqlManager.getInstance().getContext(); LocalDate localDate = LocalDate.now(ZoneId.of("GMT")); Date now = Date.valueOf(localDate); String sql = "select * from " + DEFAULT_COIN_KLINE_TABLE + period + " where `coin_id` = ? and `date` <= ? order " + "by `date` desc limit 240"; List<Record> records = context.fetch(sql, coinId, now); return records.stream().map(record -> { CoinKlineDO item = new CoinKlineDO(); item.setAmount((String) record.getValue("amount")); item.setOpen((String) record.getValue("open")); item.setHigh((String) record.getValue("high")); item.setLow((String) record.getValue("low")); item.setClose((String) record.getValue("close")); item.setTime(((Date) record.getValue("date")).getTime()); return item; }).collect(Collectors.toList()); }
/** * add record * * @param table * @param pairId * @param item * @return */ public static boolean addRecord(String table, int pairId, MarketKlineDO item) { DSLContext context = MysqlManager.getInstance().getContext(); InsertQuery<?> query = context.insertQuery(DSL.table(table)); query.addValue(DSL.field("`pair_id`", Integer.class), pairId); query.addValue(DSL.field("`time`", Timestamp.class), new Timestamp(item.getTime())); query.addValue(DSL.field("`open`", String.class), item.getOpen()); query.addValue(DSL.field("`high`", String.class), item.getHigh()); query.addValue(DSL.field("`low`", String.class), item.getLow()); query.addValue(DSL.field("`close`", String.class), item.getClose()); query.addValue(DSL.field("`amount`", String.class), item.getAmount()); query.addValue(DSL.field("`create_at`", Timestamp.class), new Timestamp(System.currentTimeMillis())); return query.execute() > 0; }
/** * add record * * @param table * @param pairId * @param items * @return */ public static boolean addRecords(String table, int pairId, List<MarketKlineDO> items) { final boolean[] addRet = {true}; MysqlManager.transaction(() -> { for (MarketKlineDO item : items) { boolean ret = addRecord(table, pairId, item); if (!ret) { addRet[0] = false; break; } } }); return addRet[0]; }