Android应用连接MS SQL数据库失败求解决方案(附代码)
Android连接MS SQL Server实现登录功能的问题修复方案
一、代码中的关键错误
- 驱动类名拼写错误:原代码中
net.sourceforge.jtds.jtbc.Driver是错误的,正确的JTDS驱动类名是net.sourceforge.jtds.jdbc.Driver - SQL注入风险:直接拼接用户输入的账号到SQL语句中,极易引发SQL注入,必须使用参数绑定
- 无用的StrictMode配置:已经使用
AsyncTask在后台线程处理数据库操作,无需开启StrictMode.permitAll(),这会破坏Android线程规范 - 缺失核心连接类实现:代码中调用的
ConnectionHelper未提供实现,这是连接失败的核心原因之一
二、必要的前置配置
- 添加网络权限:在
AndroidManifest.xml中添加互联网权限:
<uses-permission android:name="android.permission.INTERNET"/>
- 添加JTDS依赖:在
app/build.gradle的dependencies中添加JTDS驱动:
implementation 'net.sourceforge.jtds:jtds:1.3.1'
- SQL Server端配置:
- 开启SQL Server远程连接(在SQL Server配置管理器中启用TCP/IP协议并设置端口)
- 开放对应端口的防火墙权限(默认1433,自定义端口需对应开放)
- 启用SQL Server混合身份验证模式,确保数据库账号具备远程访问权限
三、修正后的完整代码
1. ConnectionHelper连接类实现
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class ConnectionHelper { // 替换为你的数据库实际配置 private static final String IP = "你的数据库IP"; private static final String PORT = "你的数据库端口"; private static final String DATABASE_NAME = "你的数据库名"; private static final String DB_USERNAME = "数据库登录账号"; private static final String DB_PASSWORD = "数据库登录密码"; private static final String CONNECTION_URL = "jdbc:jtds:sqlserver://" + IP + ":" + PORT + "/" + DATABASE_NAME; public static Connection CONN() { Connection conn = null; try { Class.forName("net.sourceforge.jtds.jdbc.Driver"); conn = DriverManager.getConnection(CONNECTION_URL, DB_USERNAME, DB_PASSWORD); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } return conn; } }
2. 修正后的LoginActivity
import android.content.Intent; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import androidx.appcompat.app.AppCompatActivity; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class LoginActivity extends AppCompatActivity { private EditText userNameET, passwordEt; private Button loginBTN; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); userNameET = findViewById(R.id.userNameEditText); passwordEt = findViewById(R.id.passEditText); loginBTN = findViewById(R.id.loginBtn); } private class DoLoginForUser extends AsyncTask<Void, Void, String> { private final String userId; private final String userPassword; public DoLoginForUser(String userId, String userPassword) { this.userId = userId; this.userPassword = userPassword; } @Override protected void onPreExecute() { super.onPreExecute(); loginBTN.setVisibility(View.GONE); } @Override protected String doInBackground(Void... voids) { Connection connect = null; PreparedStatement ps = null; ResultSet rs = null; try { connect = ConnectionHelper.CONN(); if (connect == null) { return "无法连接到数据库"; } // 使用参数绑定避免SQL注入 String query = "SELECT password FROM testDatabase WHERE UserId = ?"; ps = connect.prepareStatement(query); ps.setString(1, userId); Log.e("query", query); rs = ps.executeQuery(); if (rs.next()) { String storedPassword = rs.getString("password"); if (storedPassword != null && storedPassword.trim().equals(userPassword)) { return "success"; } else { return "无效的凭据"; } } else { return "用户不存在"; } } catch (SQLException e) { return "错误: " + e.getMessage(); } finally { // 确保资源全部关闭 try { if (rs != null) rs.close(); if (ps != null) ps.close(); if (connect != null) connect.close(); } catch (SQLException e) { e.printStackTrace(); } } } @Override protected void onPostExecute(String result) { super.onPostExecute(result); loginBTN.setVisibility(View.VISIBLE); if ("success".equals(result)) { SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences("userdetails", 0); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString("email", userNameET.getText().toString()); editor.apply(); // 异步提交更高效 Intent i = new Intent(LoginActivity.this, MainActivity.class); startActivity(i); finish(); // 结束登录页,避免返回 } else { // 可添加Toast/Snackbar提示错误信息 // Toast.makeText(LoginActivity.this, result, Toast.LENGTH_SHORT).show(); } } } public void DoLogin(View v) { String userId = userNameET.getText().toString().trim(); String password = passwordEt.getText().toString().trim(); // 本地输入校验 if (userId.isEmpty() || password.isEmpty()) { // Toast.makeText(this, "请输入用户名和密码", Toast.LENGTH_SHORT).show(); return; } new DoLoginForUser(userId, password).execute(); } }
四、额外最佳实践
- 敏感信息存储:数据库IP、账号等敏感信息不要硬编码,可放在
strings.xml或使用Android安全存储方案 - 密码安全:数据库中不要存储明文密码,应存储加盐后的哈希值(如SHA-256),登录时对比哈希值
- 异步任务替代:
AsyncTask已被弃用,推荐使用Coroutines或WorkManager处理后台任务 - 错误提示优化:在
onPostExecute中添加明确的错误提示,帮助定位问题
内容的提问来源于stack exchange,提问作者Jesse Tee
相关产品推荐
相关产品推荐

