Modelagem e Implementação

Modelagem:
Suponha que em um modelo UML eu tenha uma única classe Categoria com um único atributo descricao.

Implementação:
E eu quero criar um JavaBean que cuide desta classe UML. É certo que a classe java vai ter um atributo private descricao e os respectivos métodos públicos Setter e Getter do atributo.
Mas e como faço para ligar os métodos dessa classe com a tabela Categoria do banco de dados?

A tabela Categoria tem apenas os campos idCategoria e Descricao.

Minhas opções:

public class Categoria {
private String descricao;

public void setDescricao(String descricao) {
    this.descricao = descricao;
}

public String getDescricao() {
    return descricao;
}

static public Categoria add(String descricao) {
    //Insere uma nova categoria no database.
    //Retorna o objeto da Categoria inserida.
}

static public void del(int idCategoria) {
    //Remove uma categoria do database.
}

static public Categoria get(int idCategoria) {
    //Retorna um objeto Categoria correspondente a categoria selecionada no database.
}

static public ArrayList getAll() {
    //Retorna todas as categorias do banco de dados em um ArrayList de objetos Categoria.
}

}

Ou os métodos estáticos da classe acima ficariam numa outra classe chamada CategoriaDatabase por exemplo??

E se eu tiver uma Subcategoria no meu sistema, os métodos de inclusão, exclusão, atualização e recuperação de subcategorias ficariam na classe de Categoria ou na classe de Subcategoria??? Quem insere uma nova subcategoria por exemplo, é um objeto Categoria ou um objeto SubCategoria?

Se puderem, me mandem a classe Categoria como deveria ser.

O que voce descreveu no item 1 foi o pattern chamado Active Record, e devo dizer que ele é bem útil quando se está trabalhando com alguma abstração da JDBC (Hibernate, OJB, JDO, qualquer que seja). Colocar os métodos estáticos em outra classe, CategoriaDatabase, me parece um tanto esquisito - você teria um “estranho” conhecendo mais sobre o seu Bean do que ele mesmo…

Qual seria a modelagem correta então?

Não existe “modelagem correta”… existem as mais indicadas para cada caso… no seu, especificamente, Active Records parecem ser uma otima ideia, ja que seu codigo fica todo no mesmo lugar…

Oi, Augusto…

Eu estava (estou) tendo um problema semelhante ao seu. Aí, eu decidi criar uma classe modelo, com toda a definição dos dados que eu precisarei. E, ao mesmo tempo, estou trabalhando num gerenciador de conexão com o BD, que terá um método estático que retornará uma instância de classe quando eu precisar.
Trocando em miúdos:

class Categoria(){
//sua definição de classe
}

class DBManager(){
//gerenciador de conexão de dados

public static Categoria getCategoria(){
//Retorna um objeto categoria, após fazer uma busca pelo BD
}
}
class MeuAplicativo(){
private DBManager manager = new DBManager();
private Categoria minhaCategoria = manager.getCategoria()
}

É mais ou menos essa a idéia… No meu caso, como tenho tudo mais ou menos estruturado, estou planejando um DBManager de forma que me dê menos trabalho conforme a base dados vá aumentando, em virtude dos aperfeiçoamentos que o aplicativo terá com o passar dos anos… Creio que seja uma solução e espero ter contribuído!

Sua solução ajudou bastante sim, obrigado! e é mais ou menos isso q eu estou pensando em fazer.

Uma única ressalva q eu tenho é: não seria melhor um DBManager para cada classe? tipo CategoriaDb, ProdutoDb, ClienteDb…?
pq se for apenas uma classe de BD, essa classe vai ficar bastante ‘entupida’ com métodos de tantas classes.

Lá vem o BLOB, o anti-pattern da classe que sabe tudo e faz tudo.

O legal é vc ter um DBAdapter, e cada objeto saber se ler e se gravar. Aí vc usa o conceito de Data Access Objects, ou objetos que fazem acesso aos dados.

É um pouco diferente de gravar objetos num banco relacional, para os quais existem milhares de APIs prontas. Mas para DAO também tem várias APIs prontas, talvez vc não precise criar uma.

De modo geral, é legal que o objeto saiba o seu próprio Id. Assim vc pode fazer coisas do tipo:

public void save() {
  // chama o estático:
  saveObject(this);
}
...
public static void saveObject(Categoria obj) {
  PreparedStatement ps = adapter.prepare("categoria.save");
  ps.setInt(1, obj.getId());
  ps.setString(2, adapter.toSQL(obj.getDescricao()));
  ps.executeUpdate();
}

Nem considerei as Exceptions nesse exemplo, mas se vc ler minhas mensagens sobre adapters aí pelos fóruns, vc liga os pontos. A beleza do código acima é que vc pode trocar de banco e tudo funciona, desde que vc tenha o adapter certo.

No exemplo, eu supus que vc tem uma variável estática adapter para a classe inteira. Nada impede que vc tenha um adapter só para aplicação…

public static void saveObject(Categoria obj) {
  DBAdapter adapter = AdapterFactory.getSingletonInstance();
  PreparedStatement ps = adapter.prepare("categoria.save");
  ...
}

… ou mesmo um para cada instância. Eu geralmente uso o mesmo na aplicação inteira, mas vc tem que considerar quantas conexões com o banco vc pode usar, se tem concorrência, etc…

No trabalho, eu já troquei uma aplicação de banco e tudo o que eu tive que fazer foi implementar de novo (numa subclasse da abstrata DBAdapter) os métodos abtratos, no caso createConnection e toSQL. Se eu tiver que mudar de volta, eu vou mexer em apenas uma linha do código:

// de:
adapter = new PostgresAdapter();
// para:
adapter = new MsSqlAdapter();

A mudança é difícil de ocorrer, mas vc sabe que o cliente tem sempre razão… :lol:

[]s

de acordo com o q vc propôs, isto estaria certo?

import java.sql.*;
import java.util.*;

public class Category {
	private int categoryId;
	private String category;
	private Connection connection = null;
	
	public void setCategoryId(int categoryId) {
		this.categoryId = categoryId;
	}
	
	public int getCategoryId() {
		return categoryId;
	}
	
	public void setCategory(String category) {
		this.category = category;
	}
	
	public String getCategory() {
		return category;
	}

	public void setConnection(Connection connection) {
		this.connection = connection;
	}

	public Connection getConnection() {
		return connection;
	}

	public void insert() throws SQLException {
		insertObject(this);
	}

	public void update() throws SQLException {
		updateObject(this);
	}

	public void delete() throws SQLException {
		deleteObject(this);
	}

	public void get() throws SQLException {
		getObject(this);
	}

	static public void insertObject(Category objCategory) throws SQLException {
		String sql = "Insert Into Categories(Category) Values(?)";
		
		PreparedStatement stmt = objCategory.getConnection().prepareStatement(sql);
		stmt.setString(1, objCategory.getCategory());
		stmt.executeUpdate();
		
		sql = "Select Max(CategoryId) From Categories";
		ResultSet rsMaxCategoryId = stmt.executeQuery(sql);
		
		objCategory.setCategoryId(rsMaxCategoryId.getInt(1));
		
		rsMaxCategoryId.close();
		stmt.close();
	}
	
	static public void updateObject(Category objCategory) throws SQLException {
		String sql = "Update Categories Set Category = ? Where CategoryId = ?";
		
		PreparedStatement stmt = objCategory.getConnection().prepareStatement(sql);
		stmt.setString(1, objCategory.getCategory());
		stmt.setInt(2, objCategory.getCategoryId());
		stmt.executeUpdate();
		stmt.close();
	}
	
	static public void deleteObject(Category objCategory) throws SQLException {
		String sql = "Delete From Categories Where CategoryId = ?";
		
		PreparedStatement stmt = objCategory.getConnection().prepareStatement(sql);
		stmt.setInt(1, objCategory.getCategoryId());
		stmt.executeUpdate();
		stmt.close();
	}
	
	static public void getObject(Category objCategory) throws SQLException {
		String sql = "Select CategoryId, Category From Categories";
		sql += " Where CategoryId = ?";
		
		PreparedStatement stmt = objCategory.getConnection().prepareStatement(sql);
		stmt.setInt(1, objCategory.getCategoryId());
		
		ResultSet rsCategory = stmt.executeQuery();
		
		if (rsCategory.next()) {
			objCategory.setCategory(rsCategory.getString("Category"));
		}
		
		rsCategory.close();
		stmt.close();
		connection.close();
	}
	
	static public ArrayList getAllCategories(Connection connection) throws SQLException {
		ArrayList categories = new ArrayList();
		
		String sql = "Select CategoryId, Category From Categories Order By Category";
		Statement stmt = connection.createStatement();
		
		ResultSet rsCategories = stmt.executeQuery(sql);
		
		while (rsCategories.next()) {
			Category objCategory = new Category();
			objCategory.setCategoryId(rsCategories.getInt("CategoryId"));
			objCategory.setCategory(rsCategories.getString("Category"));
			
			categories.add(objCategory);
		}
		
		rsCategories.close();
		stmt.close();
		
		return categories;
	}
}

o método getAllCategories deveria receber o Connection como parâmetro ou deveria receber um objeto Category como os demais métodos estáticos?

Valeu pela dica, Thiago…
Vou dar uma revisada no meu projeto e tentar usar esse padrão que você passou, realmente irá facilitar muito a minha vida.