Quando você junta duas ou mais tabelas numa consulta, está criando o produto cartesiano das duas, daí o motívo de aparecer José para cada ocorrência da coluna numérica (no seu exemplo)
A cláusula distinct em nada ajuda nete caso. Só funciona para casos em que a consulta apresentasse diversas linhas com TODAS colunas iguais, como, por exemplo,
select uf from cidades;
Se todas cidades do Brasil estão cadastradas na tabela cidade, a consulta acima retornaria >4000 linhas, enquanto que
select distinct uf from cidades;
retornará apenas uma linha por estado!
Como você tem uma hierarquia entre equipamentos e hardware, uma maneira de resolver seu problema é usar duas consultas aninhadas dentro de alguma lógica, como:
ResultSet equip = stmt.executeQuery("select * from equipamentos");
System.out.println( equip.toString("descri") + "...." );
While (equip.next() {
ResultSet hw = stmt.executeQuery("select descricao from hardware where hardware.id = " + equip.id);
while hw.next() {
System.out.println( " " + hw.toString(1) );
}
}
Isso, claro, é só uma maneira de apresentar. Se for numa Jtable, basta adaptar para criar as células no lugar dos println!