Consegui resolver o meu problema! 
Para alguém que possa passar pelo mesmo, fica a solução:
- Utilizei uma JTable nativa do JAVA;
- Criei um TableModel (MyTableModel), fazendo o override de alguns métodos, e criando outros que me davam jeito;
- Criei um CellEditor (MyCellEditor), onde fiz o override dos métodos getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) e getCellEditorValue(). Este CellEditor foi feito para o editor ser uma JComboBox;
- Para o método comboBox.setSelectedItem(value) funcionar, tive que fazer o override dos métodos equals(Object obj) e hashcode() da classe que criei.
Segue o código:
Criar TableModel, JTable e popular JTable
MyTableModel tableModelRelatoriosAspectos = new MyTableModel(new String[]{"idRelatorio", "Título do Relatório", "Periodicidade"});
tableRelatoriosAspectos = new javax.swing.JTable();
(...)
XTableColumnModel columnModelRelatoriosAspectos = new XTableColumnModel();
tableRelatoriosAspectos.setColumnModel(columnModelRelatoriosAspectos);
tableRelatoriosAspectos.setModel(tableModelRelatoriosAspectos);
tableRelatoriosAspectos.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
//Ocultar primeira coluna (ID) do JTable
columnModelRelatoriosAspectos.setColumnVisible(tableRelatoriosAspectos.getColumnModel().getColumn(0), false);
columnModelRelatoriosAspectos.getColumn(1).setPreferredWidth(200);
columnModelRelatoriosAspectos.getColumn(1).setMaxWidth(250);
jScrollPane6.setViewportView(tableRelatoriosAspectos);
for (int i = 0; i < aspecto.getListaRelatorios().size(); i++) {
Object[] dados = {
""+aspecto.getListaRelatorios().get(i).getIdRelatorio(),
aspecto.getListaRelatorios().get(i),
aspecto.getListaRelatorios().get(i).getPeriodicidadeRelatorios()
};
tableModelRelatoriosAspectos.addRow(dados);
}
//Adiciona linha em branco para o utilizador poder inserir novos relatórios
tableModelRelatoriosAspectos.addRow(new Object[]{"", new Relatorio(), ""});
(...)
//Colocar o editor por defeito na da JTable como o MyCellEditor
tableRelatoriosAspectos.setDefaultEditor(Relatorio.class, new MyCellEditor(bllRelatorios.getRelatorios()));
TableModel
import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;
import portugen.ambiente.dominio.Relatorio;
public class MyTableModel extends AbstractTableModel {
private String[] columnNames = {};
private ArrayList<Object[]> dataList = new ArrayList<Object[]>();
public MyTableModel(String[] columnNames) {
this.columnNames = columnNames;
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public int getRowCount() {
return dataList.size();
}
@Override
public String getColumnName(int col) {
return columnNames[col];
}
@Override
public Object getValueAt(int row, int col) {
// Obtem a linha, que é uma String []
Object[] linha = dataList.get(row);
// Retorna o objeto que esta na coluna
return linha[col];
}
/*
* JTable uses this method to determine the default renderer/
* editor for each cell. If we didn't implement this method,
* then the last column would contain text ("true"/"false"),
* rather than a check box.
*/
@Override
public Class getColumnClass(int c) {
if (c == 1) {
return Relatorio.class;
}
return getValueAt(0, c).getClass();
}
/*
* Don't need to implement this method unless your table's
* editable.
*/
@Override
public boolean isCellEditable(int row, int col) {
return (col == 1) ? true : false;
}
/*
* Don't need to implement this method unless your table's
* data can change.
*/
@Override
public void setValueAt(Object value, int row, int col) {
// Obtem a linha, que é uma String []
Object[] linha = dataList.get(row);
// Altera o conteudo no indice da coluna passado
switch (col) {
case 0:
linha[col] = value;
break;
case 1:
linha[col] = value;
break;
case 2:
linha[col] = value;
break;
}
// dispara o evento de celula alterada
fireTableDataChanged();
}
public void addRow( Object [] dadosLinha){
dataList.add(dadosLinha);
// Informa a jtable de que houve linhas incluidas no modelo
// Como adicionamos no final, pegamos o tamanho total do modelo
// menos 1 para obter a linha incluida.
int linha = dataList.size()-1;
fireTableRowsInserted(linha,linha);
return;
}
public void removeRow(int row){
dataList.remove(row);
// informa a jtable que houve dados deletados passando a
// linha removida
fireTableRowsDeleted(row,row);
}
}
MyCellEditor
import java.awt.Component;
import java.util.ArrayList;
import javax.swing.AbstractCellEditor;
import javax.swing.JComboBox;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
import portugen.ambiente.dominio.Relatorio;
/**
*
* @author Pedro Duarte
*/
public class MyCellEditor extends AbstractCellEditor implements TableCellEditor {
private ArrayList<Relatorio> listaRelatorios = null;
private JComboBox field = new JComboBox();
public MyCellEditor(ArrayList<Relatorio> array) {
listaRelatorios = array;
for (int i = 0; i < listaRelatorios.size(); i++) {
field.addItem(listaRelatorios.get(i));
}
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
field.setSelectedItem(value); //Deixa o editor pré-selecionado com o valor da célula
return field;
}
@Override
public Object getCellEditorValue() {
return field.getSelectedItem();
}
}
Override dos métodos equals e hashcode da minha classe
public boolean equals(Object obj) {
if(this == obj)
return true;
if((obj == null) || (obj.getClass() != this.getClass()))
return false;
// object must be Relatorio at this point
Relatorio rel = (Relatorio)obj;
if (rel.getIdRelatorio() == this.getIdRelatorio())
return true;
return false;
}
@Override
public int hashCode() {
int hash = 7;
hash = 31 * hash + idRelatorio;
hash = 31 * hash + (null == tituloRelatorio? 0 : tituloRelatorio.hashCode());
hash = 31 * hash + (null == descricaoRelatorio? 0 : descricaoRelatorio.hashCode());
hash = 31 * hash + (null == comentarios? 0 : comentarios.hashCode());
hash = 31 * hash + (null == notasSobreExecucao? 0 : notasSobreExecucao.hashCode());
hash = 31 * hash + (null == responsabilidadeExecucao? 0 : responsabilidadeExecucao.hashCode());
hash = 31 * hash + (null == periodicidadeRelatorios? 0 : periodicidadeRelatorios.hashCode());
hash = 31 * hash + (null == dataProximoRelatorio? 0 : dataProximoRelatorio.hashCode());
hash = 31 * hash + (null == destinatarioRelatorio? 0 : destinatarioRelatorio.hashCode());
hash = 31 * hash + (null == responsabilidadeRelatorio? 0 : responsabilidadeRelatorio.hashCode());
hash = 31 * hash + (null == dataActualizacaoRelatorio? 0 : dataActualizacaoRelatorio.hashCode());
hash = 31 * hash + (null == localizacaoRelatorio? 0 : localizacaoRelatorio.hashCode());
return hash;
}