[Android] Android - Spring - Oracle 연동
AndroidOracle
계정 생성
CREATE USER android IDENTIFIED BY android DEFAULT TABLESPACE USERS;
GRANT connect, resource TO android;
ALTER USER android ACCOUNT UNLOCK;
테이블 생성
CREATE TABLE users (
id VARCHAR2(255),
password VARCHAR2(255),
enabled NUMBER(1) DEFAULT 1
);
데이터 삽입
INSERT INTO users (id, password) VALUES ('dico', 'dico');
Spring
프로젝트 생성
Spring Legacy Project > Spring MVC Project (Package Name: spring.mvc.android)
pom.xml
MyBatis와 JSON을 사용하기 위한 jar 추가
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- JSON -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.1</version>
</dependency>
https://mvnrepository.com에서 직접 다운로드 가능
context.xml
Servers>Tomcat [version] Server at localhost-config에 위치.
데이터베이스 연결 정보 추가.
<Resource auth="Container"
driverClassName="oracle.jdbc.driver.OracleDriver"
maxActive="50"
maxWait="1000"
name="jdbc/Oracle11g/android"
username="android"
password="android"
type="javax.sql.DataSource"
url="jdbc:oracle:thin:@localhost:1521:xe"
/>
servlet-context.xml
Connection Pool에 접근하는 bean 생성.
<!-- MyBatis -->
<!-- DAO에서 사용할 Connection Pool 접근 변수 -->
<beans:bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<beans:constructor-arg index="0" ref="sqlSessionFactory" />
</beans:bean>
<beans:bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- Connection Pool 호출 -->
<beans:property name="dataSource" ref="dataSource" />
<!-- mapper.xml 경로 설정 -->
<beans:property name="mapperLocations" value="classpath:android/*.xml" />
</beans:bean>
<!-- 3. Connection Pool 접속 -->
<beans:bean name="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<!-- context.xml의 ConnectionPool lookup -->
<beans:property name="jndiName" value="java:comp/env/jdbc/Oracle11g/android" />
<beans:property name="resourceRef" value="true" />
</beans:bean>
Users Class
VO Class 생성.
public class Users {
private String id;
private String password;
private int enabled;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getEnabled() {
return enabled;
}
public void setEnabled(int enabled) {
this.enabled = enabled;
}
}
DAO
public interface AndroidDAO {
public String confirmUser(Users user);
}
@Repository
public class AndroidDAOImpl implements AndroidDAO {
@Autowired
SqlSession sqlSession;
@Override
public String confirmUser(Users user) {
AndroidDAO mapper = sqlSession.getMapper(AndroidDAO.class);
String enabled = mapper.confirmUser(user);
return enabled;
}
}
Mapper
src>main>resources>android>mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="spring.mvc.android.persistence.AndroidDAO">
<select id="confirmUser" resultType="String">
SELECT enabled
FROM users
WHERE id = #{id} AND password = #{password}
</select>
</mapper>
Controller
@Controller
public class AndroidController {
private Logger log = Logger.getLogger(this.getClass());
@Autowired
AndroidDAO dao;
@ResponseBody
@RequestMapping("androidSignIn")
public Map<String, Object> android(HttpServletRequest req) {
log.info("androidSignIn()");
String id = req.getParameter("id");
String pwd = req.getParameter("pwd");
Users user = new Users();
user.setId(id);
user.setPassword(pwd);
String enabled = dao.confirmUser(user);
if(enabled == null) {
enabled = "0";
}
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", id);
map.put("password", pwd);
map.put("enabled", enabled);
return map;
}
}
Android
AndroidManifest.xml
인터넷 권한 부여.
<uses-permission android:name="android.permission.INTERNET" />
첫 시작 Activity를 바꿀 경우 수정.
<application
...>
<activity android:name=".SignInActivity">
...
</activity>
</application>
build.gradle(Module: app)
GSON(JSON을 편하게 사용할 수 있도록 google에서 만든 라이브러리) 추가
implementation 'com.google.code.gson:gson:2.8.2'
Users.java
Spring의 Users Class와 동일
activity_signin.xml
로그인 화면 구성
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/edt_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:layout_marginStart="32dp"
android:layout_marginTop="96dp"
android:ems="10"
android:hint="ID"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/edt_pwd"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:layout_marginStart="32dp"
android:ems="10"
android:hint="PASSWORD"
android:inputType="textPassword"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edt_id" />
<Button
android:id="@+id/btn_signin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:background="@android:color/holo_blue_dark"
android:text="SignIn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/edt_pwd" />
</android.support.constraint.ConstraintLayout>
Web.java
Task를 각 Activity마다 Inner Class로 사용하므로 공통 부분을 따로 뺐다.
public class Web {
public static String ip = "[본인IP]";
public static String servletURL = "http://" + ip + ":[포트번호]/android/"; //URL 공통 부분
}
CMD 명령어 ipconfig
HttpClient.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class HttpClient {
private int httpStatusCode; //HTTP 응답 상태 코드. (ex: 200, 401, 식별 불가능하면 -1)
private String body;
public int getHttpStatusCode() {
return httpStatusCode;
}
public String getBody() {
return body;
}
private Builder builder;
private void setBuilder(Builder builder) {
this.builder = builder;
}
//HTTP 요청 결과 수집 후 연결 해제
public void request() {
HttpURLConnection conn = getConnection();
setHeader(conn);
setBody(conn);
httpStatusCode = getStatusCode(conn);
body = readStream(conn); //요청 결과
conn.disconnect(); //연결 해제
}
private HttpURLConnection getConnection() {
try {
URL url = new URL(builder.getUrl());
return (HttpURLConnection) url.openConnection();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private void setHeader(HttpURLConnection conn) {
setContentType(conn);
setRequestMethod(conn);
conn.setConnectTimeout(30 * 1000); //서버 연결 시간 제한. 지정된 시간 안에 연결되지 않으면 SocketTimeoutException 발생
conn.setReadTimeout(15 * 1000); //Input Stream으로 읽을 때의 시간 제한. 읽기 전에 시간 초과되면 SocketTimeoutException 발생
conn.setDoOutput(true); //출력 사용 여부
conn.setDoInput(true); //입력 사용 여부
}
private void setContentType(HttpURLConnection conn) {
conn.setRequestProperty("content-Type", "application/x-www-form-urlencoded");
}
private void setRequestMethod(HttpURLConnection conn) {
try {
conn.setRequestMethod(builder.getMethod());
} catch (ProtocolException e) {
e.printStackTrace();
}
}
private void setBody(HttpURLConnection conn) {
String parameter = builder.getParameter();
if (parameter != null && parameter.length() > 0) {
OutputStream outputStream = null;
try {
outputStream = conn.getOutputStream();
outputStream.write(parameter.getBytes("UTF-8"));
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private int getStatusCode(HttpURLConnection conn) {
try {
return conn.getResponseCode();
} catch (IOException e) {
e.printStackTrace();
}
return -1;
}
private String readStream(HttpURLConnection conn) {
String result = "";
BufferedReader reader = null;
//요청 결과 읽기
try {
reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
result += line;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
//전체 URL 생성하는 Builder Pattern
public static class Builder {
private Map<String, String> parameters;
private String method;
private String url;
public String getMethod() {
return method;
}
public String getUrl() {
return url;
}
public Builder(String method, String url) {
if (method == null) {
method = "GET";
}
this.method = method;
this.url = url;
this.parameters = new HashMap<>();
}
public void addOrReplace(String key, String value) {
parameters.put(key, value);
}
public void addAllParameters(Map<String, String> param) {
parameters.putAll(param);
}
public String getParameter() {
return generateParameters();
}
public String getParameter(String key) {
return this.parameters.get(key);
}
private String generateParameters() {
StringBuffer urlParameters = new StringBuffer();
Iterator keys = getKeys();
//전달할 값을 URL 형식으로 바꾸기
String key = "";
while (keys.hasNext()) {
key = (String) keys.next();
urlParameters.append(String.format("%s=%s", key, parameters.get(key)));
urlParameters.append("&");
}
String params = urlParameters.toString();
if (params.length() > 0) {
params = params.substring(0, params.length() - 1);
}
return params;
}
private Iterator getKeys() {
return this.parameters.keySet().iterator();
}
public HttpClient create() {
HttpClient client = new HttpClient();
client.setBuilder(this);
return client;
}
}
}
SignInActivity.java
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.google.gson.Gson;
import java.util.HashMap;
import java.util.Map;
public class SignInActivity extends AppCompatActivity {
private EditText edtId, edtPwd;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_signin);
edtId = findViewById(R.id.edt_id);
edtPwd = findViewById(R.id.edt_pwd);
Button btnSignIn = findViewById(R.id.btn_signin);
btnSignIn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Map<String, String> map = new HashMap<>();
map.put("id", edtId.getText().toString());
map.put("pwd", edtPwd.getText().toString());
MapTask task = new MapTask();
task.execute(map);
}
});
}
public class MapTask extends AsyncTask<Map, Integer, String> {
//doInBackground 전에 동작
@Override
protected void onPreExecute() {
super.onPreExecute();
}
//작업을 쓰레드로 처리
@Override
protected String doInBackground(Map... maps) {
//HTTP 요청 준비
HttpClient.Builder http = new HttpClient.Builder("POST", Web.servletURL + "androidSignIn");
//Parameter 전송
http.addAllParameters(maps[0]);
//HTTP 요청 전송
HttpClient post = http.create();
post.request();
//응답 상태 코드
int statusCode = post.getHttpStatusCode();
//응답 본문
String body = post.getBody(); //Spring의 Controller에서 반환한 값. JSON 형식
return body;
}
/*
doInBackground 후에 동작.
String s : doInBackground에서 반환한 body
*/
@Override
protected void onPostExecute(String s) {
//super.onPostExecute(s);
Log.d("JSON_RESULT", s);
//JSON 형식의 데이터를 Class Object로 바꿔준다.
Gson gson = new Gson();
Users user = gson.fromJson(s, Users.class);
if(user != null && user.getEnabled() != 0) {
Toast.makeText(getApplicationContext(), "로그인", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), "회원 정보가 올바르지 않습니다.", Toast.LENGTH_SHORT).show();
}
}
}
}
D/JSON_RESULT: {"password":"dico","id":"dico","enabled":"1"}