文档章节

Spring JDBC_多数据源和事务的配置

秋风醉了
 秋风醉了
发布于 2014/11/10 19:34
字数 1875
阅读 423
收藏 3
点赞 0
评论 0

Spring JDBC_多数据源和事务的配置


直接帖代码:

maven项目,以下是pom:

<dependencies>
	<dependency>
		<groupId>org.apache.tomcat</groupId>
		<artifactId>tomcat-jdbc</artifactId>
		<version>8.0.14</version>
	</dependency>

	<!--spring context -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-context</artifactId>
		<version>4.1.1.RELEASE</version>
	</dependency>

	<!--spring core -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-core</artifactId>
		<version>4.1.1.RELEASE</version>
	</dependency>

	<!--spring bean -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-beans</artifactId>
		<version>4.1.1.RELEASE</version>
	</dependency>

	<!--spring aop -->

	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-aop</artifactId>
		<version>4.1.1.RELEASE</version>
	</dependency>

	<!--spring jdbc -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-jdbc</artifactId>
		<version>4.1.1.RELEASE</version>
	</dependency>

	<!-- spring tx -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-tx</artifactId>
		<version>4.1.1.RELEASE</version>
	</dependency>

	<!-- spring test -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-test</artifactId>
		<version>4.1.1.RELEASE</version>
	</dependency>

	<!-- spring-batch-core -->
	<dependency>
		<groupId>org.springframework.batch</groupId>
		<artifactId>spring-batch-core</artifactId>
		<version>3.0.2.RELEASE</version>
	</dependency>

	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.10</version>
		<scope>test</scope>
	</dependency>

	<!--aspectj -->
	<dependency>
		<groupId>org.aspectj</groupId>
		<artifactId>aspectjrt</artifactId>
		<version>1.8.2</version>
	</dependency>
	<dependency>
		<groupId>org.aspectj</groupId>
		<artifactId>aspectjweaver</artifactId>
		<version>1.8.2</version>
	</dependency>

	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.6</version>
	</dependency>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>3.8.1</version>
		<scope>test</scope>
	</dependency>
</dependencies>


主要说一下我的思路:

  1. 首先配置多个数据源,继承spring 的AbstractRoutingDataSource 实现管理多数据源

  2. 然后考虑怎么实现动态选择多数据源,使用ThreadLocal,在线程中传递参数,通过对应的数据源的key动态的选择多数据源

以上就是基本思路,但还要考虑如何管理事务。这里考虑使用@Transactional标签管理事务,当你配置好事务,并且在代码中动态选择数据源时,发现根本不好使,原因就是你开启了事务,早在你动态选择数据源之前,开启的事务就已经连接了数据库,等到执行具体的sql语句时,选择的数据源总是事务中连接的数据库,下面是DataSourceUtils.java中的一段代码,通过该代码,你可以看到具体是如何选择数据库连接的:

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
	Assert.notNull(dataSource, "No DataSource specified");

	ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
	if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
		conHolder.requested();
		if (!conHolder.hasConnection()) {
			logger.debug("Fetching resumed JDBC Connection from DataSource");
			conHolder.setConnection(dataSource.getConnection());
		}
		return conHolder.getConnection();
	}
	// Else we either got no holder or an empty thread-bound holder here.

	logger.debug("Fetching JDBC Connection from DataSource");
	Connection con = dataSource.getConnection();

	if (TransactionSynchronizationManager.isSynchronizationActive()) {
		logger.debug("Registering transaction synchronization for JDBC Connection");
		// Use same Connection for further JDBC actions within the transaction.
		// Thread-bound object will get removed by synchronization at transaction completion.
		ConnectionHolder holderToUse = conHolder;
		if (holderToUse == null) {
			holderToUse = new ConnectionHolder(con);
		}
		else {
			holderToUse.setConnection(con);
		}
		holderToUse.requested();
		TransactionSynchronizationManager.registerSynchronization(
				new ConnectionSynchronization(holderToUse, dataSource));
		holderToUse.setSynchronizedWithTransaction(true);
		if (holderToUse != conHolder) {
			TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
		}
	}

	return con;
}


好了,思路就又来了,既然在开启事务的时候就选择了数据源,那么必须要在事务开启之前选择数据源:很简单的做法,其实就是在开启事务的方法上加上切面,实现动态选择数据源。

下面是spring的配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	<aop:aspectj-autoproxy />
	<!-- 定义包的扫描规则 -->
	<context:component-scan base-package="com.lyx">
		<context:include-filter type="annotation"
			expression="org.springframework.stereotype.Service" />
		<context:include-filter type="annotation"
			expression="org.springframework.stereotype.Repository" />
		<context:include-filter type="annotation"
			expression="org.springframework.stereotype.Component" />
	</context:component-scan>

	<!--tomcat jdbc pool数据源配置 -->
	<bean id="localDataSource" class="org.apache.tomcat.jdbc.pool.DataSource"
		destroy-method="close">
		<property name="poolProperties">
			<bean class="org.apache.tomcat.jdbc.pool.PoolProperties">
				<property name="driverClassName" value="com.mysql.jdbc.Driver" />
				<property name="url"
					value="jdbc:mysql://10.255.9.79:3306/local_database" />
				<property name="username" value="root" />
				<property name="password" value="034039" />
			</bean>
		</property>
	</bean>

	<!--tomcat jdbc pool数据源配置 -->
	<bean id="threadDataSource" class="org.apache.tomcat.jdbc.pool.DataSource"
		destroy-method="close">
		<property name="poolProperties">
			<bean class="org.apache.tomcat.jdbc.pool.PoolProperties">
				<property name="driverClassName" value="com.mysql.jdbc.Driver" />
				<property name="url"
					value="jdbc:mysql://10.255.9.79:3306/thread_database" />
				<property name="username" value="root" />
				<property name="password" value="034039" />
			</bean>
		</property>
	</bean>

	<!--tomcat jdbc pool数据源配置 -->
	<bean id="remoteDataSource" class="org.apache.tomcat.jdbc.pool.DataSource"
		destroy-method="close">
		<property name="poolProperties">
			<bean class="org.apache.tomcat.jdbc.pool.PoolProperties">
				<property name="driverClassName" value="com.mysql.jdbc.Driver" />
				<property name="url"
					value="jdbc:mysql://10.255.9.79:3306/remote_database" />
				<property name="username" value="root" />
				<property name="password" value="034039" />
			</bean>
		</property>
	</bean>

	<!--使用枚举类型作为key -->
	<bean id="dataSource" class="com.lyx.DynamicDataSource">
		<property name="targetDataSources">
			<map key-type="com.lyx.DataSourceLookupKey">
				<entry key="REMOTE_DATASOURCE" value-ref="remoteDataSource" />
				<entry key="THREAD_DATASOURCE" value-ref="threadDataSource" />
				<entry key="LOCAL_DATASOURCE" value-ref="localDataSource" />
			</map>
		</property>
		<property name="defaultTargetDataSource" ref="localDataSource" />
	</bean>

	<!-- spring的事务管理器 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<!-- 动态选择数据源的切面 -->
	<bean id = "determineDataSourceAspect" class="com.lyx.DetermineDataSourceAspect"/>
	<!-- 切面的配置 -->
	<aop:config>
		<aop:aspect id="determineDataSourceAspect" ref="determineDataSourceAspect">
			<aop:pointcut id="dataSourcePoint"
				expression="@annotation(com.lyx.RequireDataSource)" />
			<aop:before method="determineDataSource" pointcut-ref="dataSourcePoint" />
		</aop:aspect>
	</aop:config>

	<tx:annotation-driven transaction-manager="transactionManager" />

</beans>

看到这里,你可能会问,为什么没有使用aspect的风格来配置切面,实现aop,答案就是那种方式不好用,@Transactional代理比我配置的注解的代理要提前一步,导致动态选择数据源的切面发挥不了作用。而这种方式就可以很好的工作。。


相关的类:

DataSourceLookupKey.java

package com.lyx;

/**
 * 
 * @author Lenovo
 */
public enum DataSourceLookupKey {

	LOCAL_DATASOURCE("本地数据库"), REMOTE_DATASOURCE("远程数据库"), THREAD_DATASOURCE(
			"THREAD数据库");

	private String value;

	private DataSourceLookupKey(String value) {
		this.value = value;
	}

	@Override
	public String toString() {
		return this.value;
	}

}

DbContextHolder.java

package com.lyx;

/**
 * 
 * @author Lenovo
 */
public class DbContextHolder {
	// 利用ThreadLocal解决线程安全问题
	private static final ThreadLocal<DataSourceLookupKey> contextHolder = new ThreadLocal<DataSourceLookupKey>();

	// 设置数据源
	public static void setDbType(DataSourceLookupKey dbType) {
		contextHolder.set(dbType);
	}

	// 获取数据源
	public static DataSourceLookupKey getDbType() {
		return (DataSourceLookupKey) contextHolder.get();
	}

	// 清空
	public static void clearDbType() {
		contextHolder.remove();
	}
}

DynamicDataSource.java

package com.lyx;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

	@Override
	protected DataSourceLookupKey determineCurrentLookupKey() {
		// TODO Auto-generated method stub
		if (!(DbContextHolder.getDbType() == null)) {
			System.out.println("====>" + DbContextHolder.getDbType());
		} else {
			System.out.println("====>没有初始化数据库上下文环境");
		}

		return DbContextHolder.getDbType();
	}
}

DetermineDataSourceAspect.java

package com.lyx;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

public class DetermineDataSourceAspect {

	public void determineDataSource(JoinPoint point) {
		System.out
				.println("determine datasource begin>>>>>>>>>>>>>>>>>>>>>>>>>");
		RequireDataSource datasource = ((MethodSignature) point.getSignature())
				.getMethod().getAnnotation(RequireDataSource.class);
		System.out.println("====>" + datasource.name());
		DbContextHolder.setDbType(datasource.name());
		System.out.println("determine datasource end>>>>>>>>>>>>>>>>>>>>>>>>>");
	}

}

RequireDataSource.java

package com.lyx;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface RequireDataSource {

	/**
	 * the name of datasource
	 * 
	 * @return
	 */
	DataSourceLookupKey name();
}

PersonDao.java

package com.lyx;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

@Repository
public class PersonDao {

	private JdbcTemplate jdbcTemplate;

	@Autowired
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	/**
	 * 查询记录的个数
	 * 
	 * @return
	 */
	public long count() {
		long rowCount = this.jdbcTemplate.queryForObject(
				"select count(*) from people", Long.class);
		return rowCount;
	}

	/**
	 * 查询同名的人的个数
	 * 
	 * @param name
	 * @return
	 */
	public long countPeopleByName(String name) {
		long countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
				"select count(*) from people where first_name = ?", Long.class,
				name);
		return countOfActorsNamedJoe;
	}

	/**
	 * 根据Id查询姓名
	 * 
	 * @param id
	 * @return
	 */
	public String queryNameById(int id) {
		String lastName = this.jdbcTemplate.queryForObject(
				"select last_name from t_actor where person_id = ?",
				new Object[] { id }, String.class);
		return lastName;
	}

	/**
	 * 根据Id查找实体
	 * 
	 * @param id
	 * @return
	 */
	public People findPeopleById(int id) {
		People people = this.jdbcTemplate.queryForObject(
				"select first_name, last_name from people where person_id = ?",
				new Object[] { id }, new RowMapper<People>() {
					public People mapRow(ResultSet rs, int rowNum)
							throws SQLException {
						People p = new People();
						p.setFirstName(rs.getString("first_name"));
						p.setLastName(rs.getString("last_name"));
						return p;
					}
				});

		return people;
	}

	/**
	 * 分页查询people
	 * 
	 * @param pageNo
	 * @param pageSize
	 * @return
	 */
	public List<People> findPeopleList(int pageNo, int pageSize) {
		int start = (pageNo - 1) * pageSize;
		List<People> peoples = this.jdbcTemplate.query(
				"select first_name, last_name from people limit ?,?",
				new Object[] { start, pageSize }, new RowMapper<People>() {
					public People mapRow(ResultSet rs, int rowNum)
							throws SQLException {
						People p = new People();
						p.setFirstName(rs.getString("first_name"));
						p.setLastName(rs.getString("last_name"));
						return p;
					}
				});

		return peoples;
	}

	/**
	 * 分页查询
	 * 
	 * @param pageNo
	 * @param pageSize
	 * @return
	 */
	public List<People> findPeopleList0(int pageNo, int pageSize) {
		int start = (pageNo - 1) * pageSize;
		List<People> peoples = this.jdbcTemplate.query(
				"select first_name, last_name from people limit ?,?",
				new Object[] { start, pageSize }, new PeopleRowMapper());

		return peoples;
	}

	private static class PeopleRowMapper implements RowMapper<People> {
		public People mapRow(ResultSet rs, int rowNum) throws SQLException {
			// TODO Auto-generated method stub
			People p = new People();
			p.setFirstName(rs.getString("first_name"));
			p.setLastName(rs.getString("last_name"));
			return p;
		}
	}

	/**
	 * 插入实体
	 * 
	 * @param people
	 */
	public void addPeople(People people) {
		this.jdbcTemplate.update(
				"insert into people (first_name, last_name) values (?, ?)",
				people.getFirstName(), people.getLastName());
	}

	/**
	 * 更新实体
	 * 
	 * @param id
	 */
	public void updatePeopleName(int id, String name) {
		this.jdbcTemplate
				.update("update people set last_name = ? where person_id = ?",
						name, id);
	}

	/**
	 * 删除实体
	 * 
	 * @param id
	 */
	public void deletePeople(int id) {
		this.jdbcTemplate.update("delete from people where person_id = ?", id);
	}

	/**
	 * 执行sql语句
	 * 
	 * @param sql
	 *            create table mytable (id integer, name varchar(100))
	 */
	public void execute(String sql) {
		this.jdbcTemplate.execute(sql);
	}
}

PersonService.java

package com.lyx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class PersonService {

	@Autowired
	private PersonDao personDao;

	@RequireDataSource(name = DataSourceLookupKey.REMOTE_DATASOURCE)
	@Transactional
	public void addperson() {
		System.out.println("-----addPerson begin-----");
		System.out.println("====>" + DbContextHolder.getDbType());
		People people = new People();
		people.setFirstName("adasdfasdfwe");
		people.setLastName("adadfeexcsdwadfsafd");
		this.personDao.addPeople(people);
		System.out.println("-----addPerson end-----");
		System.out.println("====>" + DbContextHolder.getDbType());
	}
}


测试类:

AppMain.java

package com.lyx;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.jdbc.datasource.DataSourceUtils.ConnectionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;

public class AppMain {

	public static void main(String[] args) {
		@SuppressWarnings("resource")
		ApplicationContext context = new ClassPathXmlApplicationContext(
				new String[] { "classpath:spring-dao.xml" });
		PersonService personService = (PersonService) context
				.getBean("personService");
		personService.addperson();
	}
	

}

结果:

determine datasource begin>>>>>>>>>>>>>>>>>>>>>>>>>

====>远程数据库

determine datasource end>>>>>>>>>>>>>>>>>>>>>>>>>

====>远程数据库

-----addPerson begin-----

====>远程数据库

-----addPerson end-----

====>远程数据库

如果你要动态选择数据库,就在该方法上加上注解@RequireDataSource,name值为数据库的枚举值。。


============================END============================


© 著作权归作者所有

共有 人打赏支持
秋风醉了
粉丝 229
博文 577
码字总数 407134
作品 0
朝阳
程序员
SSH框架之Spring4专题4:Spring与DAO

本专题内容主要包含两部分:Spring所使用的操作数据库的技术之一,JDBC模版的使用;另一部分则为Spring对于事务的管理。 Spring与Dao部分,是Spring的两大核心技术loC与AOP的经典应用体现: ...

糖醋白糖
06/26
0
0
[转]Spring引用Tomcat的 JTA事务

Spring引用Tomcat的JTA事务 Tomcat是Servlet容器,但它提供了JNDI的实现,因此用户可以象在Java EE应用程序服务器中一样,在Tomcat中使用JNDI查找JDBC数据源。在事务处理方面,Tomcat本身并不...

穿越星辰
2011/06/03
0
0
Spring引用Tomcat的 JTA事务

Spring引用Tomcat的 JTA事务 Tomcat是Servlet容器,但它提供了JNDI的实现,因此用户可以象在Java EE应用程序服务器中一样,在Tomcat中使用JNDI查找JDBC数据源。在事务处理方面,Tomcat本身并...

崔耀升
2010/06/12
0
0
Spring的事务管理难点剖析(5):联合军种作战的混乱

Spring抽象的DAO体系兼容多种数据访问技术,它们各有特色,各有千秋。像Hibernate是非常优秀的ORM实现方案,但对底层SQL的控制不 太方便;而iBatis则通过模板化技术让你方便地控制SQL,但没有...

icheer
2012/07/17
0
0
基于可靠消息方案的分布式事务(二):Java中的事务

前言:在上一篇文章 基于可靠消息方案的分布式事务:Lottor介绍 中介绍了常见的分布式事务的解决方案以及笔者基于可靠消息方案实现的分布式事务组件Lottor的原理,并展示了应用的控制台管理。...

aoho
06/01
0
0
Hasor JDBC 的难关,嵌套事务处理思路

本文存属提醒我自己不要忘记的事情。也是向大家展示 Hasor 对于 JDBC 方面即将的又一个重大的进步。目前该方案还在实施中。 前段时间闲着没事分析了下 Spring JDBC ,觉得 Spring JDBC 的设计...

哈库纳
2013/12/29
0
9
J2EE 框架 - Spring

Spring Framework 是一个开源的 Java/Java EE 全功能栈(full-stack)的应用程序框架,以 Apache 许可证形式发布,也有 .NET 平台上的移植版本。该框架基于 Expert One-on-One Java EE Desi...

匿名
2008/09/07
0
77
Spring MVC 使用 JNDI 配置的DataSource

稍微看了下,Spring 中JNDI 的使用,弄了个小例子。有很多不完备的地方,以后慢慢看,再改吧。 <一> 技术使用 Spring MVC JDBC Template Maven JNDI <二> 一些配置 Maven POM 配置 spring-c...

平江夜弹
2015/06/29
0
0
Spring Transaction + MyBatis SqlSession事务管理机制研究学习

原文地址:Spring Transaction + MyBatis SqlSession事务管理机制研究学习 线上的系统中,使用的是Spring+Mybatis+Mysql搭建的框架,由于客户需要,最近一直在对性能提升部分进行考虑,主要是...

Realfighter
2015/01/10
0
1
Mybatis学习(3)—— 事务机制

事务的实现 对数据库的事务而言,应该具有以下几点:创建(create)、提交(commit)、回滚(rollback)、关闭(close)。对应地,MyBatis将事务抽象成了Transaction接口: org.apache.ibait...

叶枫啦啦
07/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式 Factory工厂模式 Singleton单例模式 Delegate委派模式 Strategy策略模式 Prototype原型模式 Template模板模式 Spring5 beans 接口实例化 代理Bean操作 ...

小致dad
15分钟前
0
0
SpringBoot | 第十章:Swagger2的集成和使用

前言 前一章节介绍了mybatisPlus的集成和简单使用,本章节开始接着上一章节的用户表,进行Swagger2的集成。现在都奉行前后端分离开发和微服务大行其道,分微服务及前后端分离后,前后端开发的...

oKong
今天
9
0
Python 最小二乘法 拟合 二次曲线

Python 二次拟合 随机生成数据,并且加上噪声干扰 构造需要拟合的函数形式,使用最小二乘法进行拟合 输出拟合后的参数 将拟合后的函数与原始数据绘图后进行对比 import numpy as npimport...

阿豪boy
今天
12
0
云拿 无人便利店

附近(上海市-航南路)开了家无人便利店.特意进去体验了一下.下面把自己看到的跟大家分享下. 经得现场工作人员同意后拍了几张照片.从外面看是这样.店门口的指导里强调:不要一次扫码多个人进入....

周翔
昨天
1
0
Java设计模式学习之工厂模式

在Java(或者叫做面向对象语言)的世界中,工厂模式被广泛应用于项目中,也许你并没有听说过,不过也许你已经在使用了。 简单来说,工厂模式的出现源于增加程序序的可扩展性,降低耦合度。之...

路小磊
昨天
203
1
npm profile 新功能介绍

转载地址 npm profile 新功能介绍 npm新版本新推来一个功能,npm profile,这个可以更改自己简介信息的命令,以后可以不用去登录网站来修改自己的简介了 具体的这个功能的支持大概是在6这个版...

durban
昨天
1
0
Serial2Ethernet Bi-redirection

Serial Tool Serial Tool is a utility for developing serial communications, custom protocols or device testing. You can set up bytes to send accordingly to your protocol and save......

zungyiu
昨天
1
0
python里求解物理学上的双弹簧质能系统

物理的模型如下: 在这个系统里有两个物体,它们的质量分别是m1和m2,被两个弹簧连接在一起,伸缩系统为k1和k2,左端固定。假定没有外力时,两个弹簧的长度为L1和L2。 由于两物体有重力,那么...

wangxuwei
昨天
0
0
apolloxlua 介绍

##项目介绍 apolloxlua 目前支持javascript到lua的翻译。可以在openresty和luajit里使用。这个工具分为两种模式, 一种是web模式,可以通过网页使用。另外一种是tool模式, 通常作为大规模翻...

钟元OSS
昨天
2
0
Mybatis入门

简介: 定义:Mybatis是一个支持普通SQL查询、存储过程和高级映射的持久层框架。 途径:MyBatis通过XML文件或者注解的形式配置映射,实现数据库查询。 特性:动态SQL语句。 文件结构:Mybat...

霍淇滨
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部