当前位置: 首页 > news >正文

校园网站开发哪里有网站建设加工

校园网站开发,哪里有网站建设加工,北京云无限优化,有哪些网站可以找兼职做前言 在进行分析之前#xff0c;建议快速浏览之前写的理解MyBatis原理、思想#xff0c;这样更容易阅读、理解本篇内容。 验证一级缓存 MyBatis的缓存有两级#xff0c;一级缓存默认开启#xff0c;二级缓存需要手动开启。 重复读取跑缓存 可以看到#xff0c;第二次…前言 在进行分析之前建议快速浏览之前写的理解MyBatis原理、思想这样更容易阅读、理解本篇内容。 验证一级缓存 MyBatis的缓存有两级一级缓存默认开启二级缓存需要手动开启。 重复读取跑缓存 可以看到第二次请求的时候没有打印SQL而是使用了缓存。 Test public void test1() throws IOException {String resource mybatis-config.xml;InputStream inputStream Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession1 sqlSessionFactory.openSession(true);SysRoleMapper mapper1 sqlSession1.getMapper(SysRoleMapper.class);SysRole role mapper1.getById(2);SysRole role2 mapper1.getById(2);System.out.println(role);System.out.println(role2); }//------------------------------打印SQL-------------------------------------- Preparing: select * from sys_role where role_id ?Parameters: 2(Integer)Columns: role_id, role_name, role_key, role_sort, data_scope, status, del_flag, create_by, create_time, update_by, update_time, remarkRow: 2, 测试2, common, 2, 2, 0, 0, admin, 2022-08-29 15:58:05, , null, 普通角色Total: 1 SysRole{role_id2, role_name测试2}SysRole{role_id2, role_name测试2}同一会话的更新操作刷新缓存 通过测试结果可以看到因为更新操作的原因两次查询都查了数据库。 Test public void test2() throws IOException {String resource mybatis-config.xml;InputStream inputStream Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession1 sqlSessionFactory.openSession(true);SysRoleMapper mapper1 sqlSession1.getMapper(SysRoleMapper.class);SysRole role mapper1.getById(2);mapper1.updateRoleNameById(测, 2);SysRole role2 mapper1.getById(2);System.out.println(role);System.out.println(role2); }//------------------------------打印SQL-------------------------------------- Preparing: select * from sys_role where role_id ?Parameters: 2(Integer)Columns: role_id, role_name, role_key, role_sort, data_scope, status, del_flag, create_by, create_time, update_by, update_time, remarkRow: 2, 测试2, common, 2, 2, 0, 0, admin, 2022-08-29 15:58:05, , null, 普通角色Total: 1 Preparing: update sys_role set role_name ? where role_id ?Parameters: 测(String), 2(Integer)Updates: 1 Preparing: select * from sys_role where role_id ?Parameters: 2(Integer)Columns: role_id, role_name, role_key, role_sort, data_scope, status, del_flag, create_by, create_time, update_by, update_time, remarkRow: 2, 测, common, 2, 2, 0, 0, admin, 2022-08-29 15:58:05, , null, 普通角色Total: 1SysRole{role_id2, role_name测试2} SysRole{role_id2, role_name测}跨会话更新数据没有刷新缓存 Test public void test() throws IOException {String resource mybatis-config.xml;InputStream inputStream Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream);// 会话一System.out.println(会话一);SqlSession sqlSession1 sqlSessionFactory.openSession(true);SysRoleMapper mapper1 sqlSession1.getMapper(SysRoleMapper.class);SysRole role mapper1.getById(2);System.out.println(role);// 会话二System.out.println(会话二);SqlSession sqlSession2 sqlSessionFactory.openSession(true);SysRoleMapper mapper2 sqlSession2.getMapper(SysRoleMapper.class);mapper2.updateRoleNameById(测试2, 2);System.out.println(mapper2.getById(2));// 会话一重新查询System.out.println(会话一重新查询);role mapper1.getById(2);System.out.println(role);}//------------------------------打印结果--------------------------------------会话一 SysRole{role_id2, role_name测试} 会话二 SysRole{role_id2, role_name测试2} 会话一重新查询 SysRole{role_id2, role_name测试}源码分析的入口点 我们要阅读、分析源码就需要先找准一个切入点我们以下面代码为例子SysRoleMapper#getById()方法作为调试入口 Test public void test1() throws IOException {String resource mybatis-config.xml;InputStream inputStream Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession1 sqlSessionFactory.openSession(true);SysRoleMapper mapper1 sqlSession1.getMapper(SysRoleMapper.class);// 调试入口SysRole role mapper1.getById(2);SysRole role2 mapper1.getById(2);System.out.println(role);System.out.println(role2); } 在分析之前我们就先约定一下符号表示你的视角要焦距在哪几行代码。 一级缓存流程分析 好现在我们开始分析一级缓存的流程了解其设计思想看看能学到什么。 MapperProxy 首先我们可以看到通过getMapper方法拿到的对象mapper1其实是一个代理对象MapperProxy的实例。 MapperProxy实现了InvocationHandler接口所以SysRoleMapper调用的 方法 都会进入代理对象MapperProxy的invoke方法。 public class MapperProxyT implements InvocationHandler, Serializable {// 略Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 先拿到声明method方法的类在这里具体指定是SysRoleMapper。// 如果是 Object 类则表明调用的是一些通用方法比如 toString()、hashCode() 等就直接调用即可。if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else {return cachedInvoker(method).invoke(proxy, method, args, sqlSession);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}// 略}小结从上面可以知道我们调用SysRoleMapper接口中的 方法其实都会进入MapperProxy#invoke方法中。 现在我们进一步看由于getById方法不是Object默认的方法所以会跑else分支详情分析请看代码 Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else {// 跑else分支// 整体了解此行代码的流程// 1.首先Method会被包装成MapperMethod1️⃣// 2.MapperMethod被封装到PlainMethodInvoker类内2️⃣// 3.此类(PlainMethodInvoke)提供一个普通的方法invoke此方法会实际调用MapperMethod的execute方法3️⃣ return cachedInvoker(method).invoke(proxy, method, args, sqlSession);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {try {return MapUtil.computeIfAbsent(methodCache, method, m - {// 是否Java语言规范定义的默认方法否if (m.isDefault()) {// 这里的细节不要深究了try {if (privateLookupInMethod null) {return new DefaultMethodInvoker(getMethodHandleJava8(method));} else {return new DefaultMethodInvoker(getMethodHandleJava9(method));}} catch (IllegalAccessException | InstantiationException | InvocationTargetException| NoSuchMethodException e) {throw new RuntimeException(e);}} else { // 看这里跑的是else分支// 对于普通的方法(如SysRoleMapper#getById)使用的是PlainMethodInvoker实现类。// 其中MapperMethod表示对原始的Method方法对象进行了一次包装细节就先不深究了// mapperInterface 信息在创建MapperProxy对象的时候写入信息默认来源于我们定义的mybatis-config.xml文件 包括sqlSession也是。return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())1️⃣);}});} catch (RuntimeException re) {Throwable cause re.getCause();throw cause null ? re : cause;}}private static class PlainMethodInvoker implements MapperMethodInvoker {private final MapperMethod mapperMethod;public PlainMethodInvoker(MapperMethod mapperMethod) {super();this.mapperMethod mapperMethod;2️⃣}Overridepublic Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {return mapperMethod.execute(sqlSession, args);3️⃣}}从上面注释中相信你已经了解到MapperProxy#invoke方法下一步会流向哪个类MapperMethod#execute() MapperMethod 现在我们看看MapperMethod#execute()做了什么根据command属性提供的sql方法类型调用sqlSession接口中合适的的处理方法。 public class MapperMethod {// 方法对应的sql类型select、update、delete、insert// 在MapperProxy#invoke#cachedInvoker方法中创建MapperMethod类时设置的感兴趣的可以回看private final SqlCommand command; private final MethodSignature method;public MapperMethod(Class? mapperInterface, Method method, Configuration config) {this.command new SqlCommand(config, mapperInterface, method);this.method new MethodSignature(config, mapperInterface, method);}// 这个方法整体做了什么根据command提供的sql方法类型调用sqlSession接口中合适的的处理方法。// 我们之前封装MapperMethod的时候定义了此类的command、method属性// 其中command这个属性表示sql方法的类型 public Object execute(SqlSession sqlSession, Object[] args) {Object result;// getById方法是查询语句所以会进入SELECT分支switch (command.getType()) {case INSERT: {Object param method.convertArgsToSqlCommandParam(args);result rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param method.convertArgsToSqlCommandParam(args);result rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param method.convertArgsToSqlCommandParam(args);result rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() method.hasResultHandler()) { // 无返回值同时有专门的结果处理类executeWithResultHandler(sqlSession, args);result null;} else if (method.returnsMany()) { // 返回多个结果result executeForMany(sqlSession, args);} else if (method.returnsMap()) { // 返回map类型的结果result executeForMap(sqlSession, args);} else if (method.returnsCursor()) { // 返回结果是数据库游标类型result executeForCursor(sqlSession, args);} else { // 看这里跑的是else分支// 获取参数对象不用关注细节Object param method.convertArgsToSqlCommandParam(args);// SysRoleMapper#getById结果类型是单个对象所以最终跑的是这行代码result sqlSession.selectOne(command.getName(), param);// 下面代码细节不重要就不展开了if (method.returnsOptional() (result null || !method.getReturnType().equals(result.getClass()))) {result Optional.ofNullable(result);}}break;case FLUSH:result sqlSession.flushStatements();break;default:throw new BindingException(Unknown execution method for: command.getName());}if (result null method.getReturnType().isPrimitive() !method.returnsVoid()) {throw new BindingException(Mapper method command.getName() attempted to return null from a method with a primitive return type ( method.getReturnType() ).);}return result;}// 略}好了看完上面代码相信你已经知道下一步代码会跑到那里了SqlSession#selectOne() SqlSession是一个接口定义了一些列通用的SQL操作如selectList、insert、update、commit 和 rollback等操作。 小结通过上面的分析我们已经知道我们调用SysRoleMapper#getById方法本质上其实还是调用SqlSession接口提供的通用SQL操作方法。只不过利用 代理 Mapper接口 的方式实现方法调用 自动路由到SqlSession接口对应的方法。 SqlSession 通过上面分析想必你已经知道下一步要走哪了SqlSession接口默认的实现类是DefaultSqlSession所以selectOne方法跑的是这个实现类 public class DefaultSqlSession implements SqlSession {// 略Overridepublic T T selectOne(String statement, Object parameter) {// Popular vote was to return null on 0 results and throw exception on too many.译大众投票是在 0 个结果上返回 null并在太多结果上抛出异常。// 很明显selectOne最终跑的是selectList方法ListT list this.selectList(statement, parameter);// 下面代码不用关注if (list.size() 1) {return list.get(0);} else if (list.size() 1) {throw new TooManyResultsException(Expected one result (or null) to be returned by selectOne(), but found: list.size());} else {return null;}}// 略/*** 封装MappedStatement对象通过executor发起查询。* param statement 映射信息方法的全路径cn.lsj.seckill.SysRoleMapper.getById* param parameter SQL参数* param rowBounds 辅助分页默认不分页。RowBounds(int offset, int limit)* param handler 处理结果回调。查询完成之后调用回调* return* param E*/private E ListE selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {try {// 这个类主要封装了SysRoleMapper相关信息包括方法全路径id、原始xml文件resource、// sql语句相关信息sqlSource、结果类型映射信息、与映射语句关联的缓存配置信息cache等MappedStatement ms configuration.getMappedStatement(statement);// wrapCollection是懒加载机制的一部分不用关注细节return executor.query(ms, wrapCollection(parameter), rowBounds, handler);} catch (Exception e) {throw ExceptionFactory.wrapException(Error querying database. Cause: e, e);} finally {ErrorContext.instance().reset();}}}通过上述代码可以知道selectOne方法内部最终还是依靠Executor接口的query方法去执行具体的sql只不过在此之前会从Configuration配置类里面通过 映射信息 statement 拿到MappedStatement封装对象然后传递给query方法。 Executor 在上面我们了解到下一步走的是Executor接口的query方法CachingExecutor是Executor接口的实现类基于装饰者模式对Executor功能进行了增强增加了缓存机制。 public class CachingExecutor implements Executor {private final Executor delegate; // 默认被装饰的实现类 SimpleExecutorprivate final TransactionalCacheManager tcm new TransactionalCacheManager();public CachingExecutor(Executor delegate) {this.delegate delegate;delegate.setExecutorWrapper(this);}// 略Overridepublic E ListE query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {// 表示一条 SQL 语句以及相关参数不用关注细节BoundSql boundSql ms.getBoundSql(parameterObject);// 构造缓存的KEY不用关注细节CacheKey key createCacheKey(ms, parameterObject, rowBounds, boundSql);return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}Overridepublic E ListE query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {// 读取二级缓存的缓存对象Cache cache ms.getCache();// 开启二级缓存时跑这个分支先不关注if (cache ! null) {flushCacheIfRequired(ms);if (ms.isUseCache() resultHandler null) {ensureNoOutParams(ms, boundSql);SuppressWarnings(unchecked)ListE list (ListE) tcm.getObject(cache, key);if (list null) {list delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}// 通过断点可以看到默认被装饰的Executor接口实现类是SimpleExecutor 图1️⃣// 由于SimpleExecutor继承了抽象类BaseExecutor 但没有实现query方法所以最终指向的还是BaseExecutor#query() 图2️⃣ return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}// 略} 图1️⃣ 图2️⃣ 通过上面代码注释我们最终了解到CachingExecutor#query方法跑向的是BaseExecutor#query。 现在我们看一下BaseExecutor类的query方法 public abstract class BaseExecutor implements Executor {// 略protected PerpetualCache localCache; // 缓存Cache一级缓存具体的一个实现类// 略Overridepublic E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity(executing a query).object(ms.getId());if (closed) {throw new ExecutorException(Executor was closed.);}if (queryStack 0 ms.isFlushCacheRequired()) {clearLocalCache();}// 很明显这个是存储查询结果的我们围绕这个对象来看代码ListE list;try {queryStack;// 从缓存中读取结果第一次查询没有缓存list resultHandler null ? (ListE) localCache.getObject(key) : null;if (list ! null) {handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else { // 跑else分支// 从数据库中读取list queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack 0) {for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();if (configuration.getLocalCacheScope() LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;}// 略private E ListE queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ListE list;// 给key对应的缓存值设置一个占位值只是用于占位localCache.putObject(key, EXECUTION_PLACEHOLDER);try {// 真正处理查询的方法// 抽象类没有实现doQuery方法所以方法的调用是其实现类 SimpleExecutor#doQuerylist doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {localCache.removeObject(key);}localCache.putObject(key, list);if (ms.getStatementType() StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;} }StatementHandler RoutingStatementHandler 现在我们在看看SimpleExecutor#doQuery方法没有太多复杂逻辑直接是交由StatementHandler接口处理了接口的实现类是RoutingStatementHandler。 在划分上StatementHandler属于Executor的一部分参与SQL处理 RoutingStatementHandler 根据执行的 SQL 语句的类型SELECT、UPDATE、DELETE 等选择不同的 StatementHandler 实现进行处理。PreparedStatementHandler 处理预编译 SQL 语句的实现类。预编译 SQL 语句是指在数据库预先编译 SQL 语句并生成执行计划然后在后续的执行中只需要传递参数并执行编译好的执行计划可以提高 SQL 的执行效率。 Override public E ListE doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt null;try {Configuration configuration ms.getConfiguration();// 此接口用于处理数据库的 Statement 对象的创建和执行StatementHandler handler configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);stmt prepareStatement(handler, ms.getStatementLog()); return handler.query(stmt, resultHandler); // 打断点可以看到handler实现类RoutingStatementHandler它作用就是选择合适的StatementHandler实现类执行SQL} finally {closeStatement(stmt);} }我们再看看RoutingStatementHandler#query方法使用了装饰者模式被装饰类是PreparedStatementHandler。 PreparedStatementHandler RoutingStatementHandler选择了合适的处理类来执行SQLPreparedStatementHandler。 现在打开看看PreparedStatementHandler#query方法 Overridepublic E ListE query(Statement statement, ResultHandler resultHandler) throws SQLException {// Java JDBC 中的一个接口用于执行预编译的 SQL 语句。使用过JDBC编程的应该见过可以看文末的JDBC编程Demo回忆回忆。PreparedStatement ps (PreparedStatement) statement;// 执行 SQL 语句。ps.execute();// “结果处理器”会处理并返回查询结果在这里就不深究了return resultSetHandler.handleResultSets(ps);}现在让我们往回看BaseExecutor#queryFromDatabase方法 private E ListE queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ListE list;// 给key对应的缓存值设置一个占位值只是用于占位localCache.putObject(key, EXECUTION_PLACEHOLDER);try {// 此时我们已经拿到结果了list doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {localCache.removeObject(key);}// 将结果写入到缓存中 localCache.putObject(key, list);if (ms.getStatementType() StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;}到这里我们经历了一次第一次查询的过程并在BaseExecutor#queryFromDatabase方法中将查询结果写入到localCache属性中。 我们再查一次就会发现在BaseExecutor#query中这次直接拿到了缓存的数据 Overridepublic E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {// 略ListE list;try {queryStack;// 从本地缓存拿到了上次的查询结果list resultHandler null ? (ListE) localCache.getObject(key) : null;if (list ! null) {handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {list queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}// 略return list;}小结 整个流程下来发现最关键的地方就是BaseExecutor抽象类的query、queryFromDatabase这两个方法它们在一级缓存方面围绕localCache属性做缓存操作。 第一次查询跑queryFromDatabase方法并将查询结果写入localCache属性第二次相同的查询直接从localCache属性中读取缓存的查询结果。 二级缓存流程分析 开启二级缓存 添加配置到mybatis-config.xml文件 settings!-- 二级缓存--setting namecacheEnabled valuetrue/ /settings修改SysRoleMapper.xml文件 ?xml version1.0 encodingUTF-8 ? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecn.lsj.seckill.SysRoleMapper!-- 表示此namespace开启二级缓存 --cache/select idgetById resultTypecn.lsj.seckill.SysRole select * from sys_role where role_id #{id}/select/mapper流程分析 当我们开启二级缓存之后查询过程就变成二级缓存-一级缓存-数据库 二级缓存的验证代码 Testpublic void test1() throws IOException {String resource mybatis-config.xml;InputStream inputStream Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession1 sqlSessionFactory.openSession(true);SysRoleMapper mapper1 sqlSession1.getMapper(SysRoleMapper.class);SysRole role mapper1.getById(2);System.out.println(role);// 提交事务二级缓存数据才生效sqlSession1.commit();SqlSession sqlSession2 sqlSessionFactory.openSession(true);SysRoleMapper mapper2 sqlSession2.getMapper(SysRoleMapper.class);SysRole role2 mapper2.getById(2);System.out.println(role2);System.out.println(mapper1.getById(2));}在前面的CachingExecutor#query方法中我们看到了二级缓存的代码 Overridepublic E ListE query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {Cache cache ms.getCache();// 假如我们开启了二级缓存那么我们的查询会先跑此分支if (cache ! null) {flushCacheIfRequired(ms);if (ms.isUseCache() resultHandler null) {ensureNoOutParams(ms, boundSql);SuppressWarnings(unchecked)// 从缓存中读取数据 ListE list (ListE) tcm.getObject(cache, key);// 二级缓存中没有数据时再查数据库if (list null) {list delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);// 将查询结果写入到二级缓存中tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}总结 看到这里我们回顾一下在之前的分析中我们看到装饰者模式出现得比较频繁此外还是用到动态代理技术。 整个分析下来相信你收获的不止这些源码阅读能力应该能得到一些提升对设计模式、动态代理的理解也会有一些加深。 好了如果你感兴趣的话可以进一步深入分析缓存如何刷新、生效如何做到缓存会话级别、Mapper级别的隔离的。 最后留下一些思考问题 开启二级缓存之后为什么sqlSession1.commit();之后二级缓存才生效 附JDBC编程Demo import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;public class JDBCDemo {public static void main(String[] args) {// MySQL服务器的JDBC URL、用户名和密码String url jdbc:mysql://localhost:3306/你的数据库名;String user 你的用户名;String password 你的密码;try {// 加载JDBC驱动程序Class.forName(com.mysql.cj.jdbc.Driver);// 建立数据库连接Connection connection DriverManager.getConnection(url, user, password);// 创建SQL语句String sql SELECT * FROM 你的表名;PreparedStatement preparedStatement connection.prepareStatement(sql);// 执行查询ResultSet resultSet preparedStatement.executeQuery();// 处理结果集while (resultSet.next()) {int id resultSet.getInt(id);String name resultSet.getString(name);String email resultSet.getString(email);System.out.println(ID: id , Name: name , Email: email);}// 关闭资源resultSet.close();preparedStatement.close();connection.close();} catch (ClassNotFoundException | SQLException e) {e.printStackTrace();}} }
http://www.pierceye.com/news/472056/

相关文章:

  • 垂直门户网站都有什么网站首页index.html
  • wordpress网站加载效果线上推销的方法
  • 网站都有什么语言杭州网络营销公司
  • 济南高新网站制作正规seo排名外包
  • 网站方案讲解技巧ppt的免费网站
  • 个人网站名称有哪些WordPress dux修改
  • 普法网站建设方案app制作开发公司怎么收费
  • 网站平台建设哪家公司好网站建设建站在线建站
  • 龙岗区住房和建设局在线网站网站如何做团购
  • 河南省建设监理协会网站证书查询wordpress 修改链接
  • 做网站业务员怎么样深圳福田最新新闻事件
  • 衡水商城网站建设外贸汽车配件做那个网站
  • 做网站的色彩搭配的小知识群艺馆网站建设方案
  • 深圳 汽车网站建设学习网站建设培训
  • 制作手机网站用什么软件唐山网站专业制作
  • 网站后台如何登陆互联网营销中心
  • 做排行榜的网站知乎长沙服务好的网络营销
  • 做网站猫要做端口映射吗太原网站建设口碑推荐
  • 新闻门户网站是什么快速搭建网页
  • 随意设计一个网站域名是什么?
  • 找人做网站需要准备什么材料用视频做网站背景
  • 大连做网站首选领超科技wordpress注册邮件发送设置
  • 西山区城市建设局网站如何做防水网站
  • 商务网站建设的组成包括自动链接 wordpress
  • 网站如何关闭东莞网站开发推荐
  • 自己开网站能赚钱吗网站界面设计描述
  • 二手交易网站建设方案ppt网站备案的作用
  • 北京行业网站建设临沂谁会做网站
  • 网站备案 游戏修改wordpress字体
  • 福建微网站建设价格宝山专业网站建设