TX row contention is literally that, ie, for a particular row. Hash partitioning is not going to change that. For example
SQL> create table t
2 partition by hash ( object_id)
3 partitions 8
4 as select * from dba_objects
5 where object_id is not null;
Table created.
SQL>
SQL> select dbms_rowid.rowid_Object(rowid), count(*)
2 from t
3 group by dbms_rowid.rowid_Object(rowid);
DBMS_ROWID.ROWID_OBJECT(ROWID) COUNT(*)
------------------------------ ----------
162760 9947
162759 9897
162758 9851
162761 9744
162762 9743
162763 9660
162764 9948
162765 9762
8 rows selected.
SQL>
SQL> select partition_name from user_tab_partitions where table_name = 'T';
PARTITION_NAME
------------------------------
SYS_P4910
SYS_P4911
SYS_P4912
SYS_P4913
SYS_P4914
SYS_P4915
SYS_P4916
SYS_P4917
8 rows selected.
SQL>
SQL> create table gtt ( r rowid );
Table created.
SQL>
SQL> begin
2 for i in ( select partition_name from user_tab_partitions where table_name = 'T' and rownum <= 4 )
3 loop
4 execute immediate
5 'insert into gtt select rowid from t partition ('||i.partition_name||') where rownum <= 500';
6 end loop;
7 end;
8 /
PL/SQL procedure successfully completed.
SQL>
SQL> select count(*) from gtt;
COUNT(*)
----------
2000
1 row selected.
SQL>
SQL> delete from t where rowid in ( select r from gtt ) ;
2000 rows deleted.
SQL>
So I've got 8 partitions and I've deleted rows from 4 of them. In another session, I then try some deletes
SQL> delete from t partition ( SYS_P4916 );
9948 rows deleted.
That works because it does not clash with the partitions that have some active transactions. But this one:
SQL> delete from t partition ( SYS_P4912 );
[waiting]
and I'm blocked by the deletes. But these are still *row* level locks. For example, if I pick a row that was not in the deleted set, I'm fine
SQL> delete from t partition ( SYS_P4912 )
2 where object_id = 162203;
1 row deleted.
btw, you want to have an power of 2 hash partitions, otherwise your data will not be evenly spaced between them